• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

polserver / polserver / 32558794315

22 Aug 2026 07:08AM UTC coverage: 78.809% (+0.03%) from 78.778%
32558794315

push

github

web-flow
Better error reporting info (#948)

* add script information when crashing

* track threads that should be running but arent

* add per-thread checkin

117 of 136 new or added lines in 13 files covered. (86.03%)

6 existing lines in 3 files now uncovered.

57276 of 72677 relevant lines covered (78.81%)

1253471.52 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

18.09
/pol-core/clib/Debugging/ExceptionParser.cpp
1
#include "clib/Debugging/ExceptionParser.h"
2

3
#include "clib/Program/ProgramConfig.h"
4
#include "clib/logfacility.h"
5
#include "clib/scriptstatus.h"
6
#include "clib/stlutil.h"
7
#include "clib/threadhelp.h"
8
#include <pol_global_config.h>
9

10
#include <cstddef>
11
#include <cstdlib>
12
#include <cstring>
13
#include <errno.h>
14
#include <signal.h>
15
#include <stdio.h>
16
#include <string_view>
17

18
#ifndef WINDOWS
19
#include <arpa/inet.h>
20
#include <cxxabi.h>
21
#include <execinfo.h>
22
#include <netdb.h>
23
#include <netinet/in.h>
24
#include <sys/mman.h>
25
#include <unistd.h>
26

27
#define SOCKET int
28
#else
29
#include "clib/Header_Windows.h"
30
#include <shlwapi.h>
31
#endif
32

33
#include <boost/stacktrace.hpp>
34

35
#define MAX_STACK_TRACE_DEPTH 200
36
#define MAX_STACK_TRACE_STEP_LENGTH 512
37

38

39
namespace Pol::Clib
40
{
41
using namespace std;
42

43
///////////////////////////////////////////////////////////////////////////////
44

45
std::atomic<const std::atomic<size_t>*> ExceptionParser::m_worldLockOwner{ nullptr };
46
bool ExceptionParser::m_programAbortReporting = false;
47
std::string ExceptionParser::m_programAbortReportingServer = "";
48
std::string ExceptionParser::m_programAbortReportingUrl = "";
49
std::string ExceptionParser::m_programAbortReportingReporter = "";
50
std::string ExceptionParser::m_programStart = Pol::Clib::Logging::LogSink::getTimeStamp();
51

52
///////////////////////////////////////////////////////////////////////////////
53

54
namespace
55
{
56
void getSignalDescription( int signal, string& signalName, string& signalDescription )
×
57
{
58
  switch ( signal )
×
59
  {
60
  case 1:
×
61
    signalName = "SIGHUP";
×
62
    signalDescription = "hangup detected on controlling terminal or death of controlling process";
×
63
    break;
×
64
  case 2:
×
65
    signalName = "SIGINT";
×
66
    signalDescription = "interrupt from keyboard";
×
67
    break;
×
68
  case 3:
×
69
    signalName = "SIGQUIT";
×
70
    signalDescription = "quit from keyboard";
×
71
    break;
×
72
  case 4:
×
73
    signalName = "SIGILL";
×
74
    signalDescription = "illegal Instruction";
×
75
    break;
×
76
  case 6:
×
77
    signalName = "SIGABRT";
×
78
    signalDescription = "abort signal from abort()";
×
79
    break;
×
80
  case 8:
×
81
    signalName = "SIGFPE";
×
82
    signalDescription = "floating point exception";
×
83
    break;
×
84
  case 9:
×
85
    signalName = "SIGKILL";
×
86
    signalDescription = "kill signal";
×
87
    break;
×
88
  case 10:
×
89
    signalName = "SIGBUS";
×
90
    signalDescription = "bus error";
×
91
    break;
×
92
  case 11:
×
93
    signalName = "SIGSEGV";
×
94
    signalDescription = "invalid memory reference";
×
95
    break;
×
96
  case 12:
×
97
    signalName = "SIGSYS";
×
98
    signalDescription = "bad argument to system call";
×
99
    break;
×
100
  case 13:
×
101
    signalName = "SIGPIPE";
×
102
    signalDescription = "broken pipe: write to pipe with no readers";
×
103
    break;
×
104
  case 14:
×
105
    signalName = "SIGALRM";
×
106
    signalDescription = "timer signal from alarm()";
×
107
    break;
×
108
  case 15:
×
109
    signalName = "SIGTERM";
×
110
    signalDescription = "termination signal";
×
111
    break;
×
112
  case 18:
×
113
    signalName = "SIGCONT";
×
114
    signalDescription = "continue signal from tty";
×
115
    break;
×
116
  case 19:
×
117
    signalName = "SIGSTOP";
×
118
    signalDescription = "stop signal from tty";
×
119
    break;
×
120
  case 20:
×
121
    signalName = "SIGTSTP";
×
122
    signalDescription = "stop signal from user (keyboard)";
×
123
    break;
×
124
  case 16:
×
125
  case 30:
126
    signalName = "SIGUSR1";
×
127
    signalDescription = "user-defined signal 1";
×
128
    break;
×
129
  case 17:
×
130
  case 31:
131
    signalName = "SIGUSR2";
×
132
    signalDescription = "user-defined signal 2";
×
133
    break;
×
134
  default:
×
135
    signalName = "unsupported signal";
×
136
    signalDescription = "unsupported signal occurred";
×
137
    break;
×
138
  }
139
}
×
140

141
#if !defined( _WIN32 ) && !defined( __APPLE__ )
142
void logExceptionSignal( int signal )
×
143
{
144
  string signalName;
×
145
  string signalDescription;
×
146

147
  getSignalDescription( signal, signalName, signalDescription );
×
148
  printf( "Signal \"%s\"(%d: %s) detected.\n", signalName.c_str(), signal,
×
149
          signalDescription.c_str() );
150
}
×
151
#endif
152

153
string getCompilerVersion()
×
154
{
155
#ifdef LINUX
156
  char result[256];
157
#ifdef __clang__
158
  snprintf( result, arsize( result ), "clang %d.%d.%d", __clang_major__, __clang_minor__,
159
            __clang_patchlevel__ );
160
#else
161
  snprintf( result, arsize( result ), "gcc %d.%d.%d", __GNUC__, __GNUC_MINOR__,
×
162
            __GNUC_PATCHLEVEL__ );
163
#endif
164
#endif
165
#ifdef WINDOWS
166
  string result;
167
  switch ( _MSC_VER )
168
  {
169
  case 1900:
170
    result = "MSVC++ 14.0 (Visual Studio 2015)";
171
    break;
172
  case 1800:
173
    result = "MSVC++ 12.0 (Visual Studio 2013)";
174
    break;
175
  case 1700:
176
    result = "MSVC++ 11.0 (Visual Studio 2012)";
177
    break;
178
  case 1600:
179
    result = "MSVC++ 10.0 (Visual Studio 2010)";
180
    break;
181
  case 1500:
182
    result = "MSVC++ 9.0 (Visual Studio 2008)";
183
    break;
184
  case 1400:
185
    result = "MSVC++ 8.0 (Visual Studio 2005)";
186
    break;
187
  case 1310:
188
    result = "MSVC++ 7.1 (Visual Studio 2003)";
189
    break;
190
  case 1300:
191
    result = "MSVC++ 7.0";
192
    break;
193
  case 1200:
194
    result = "MSVC++ 6.0";
195
    break;
196
  case 1100:
197
    result = "MSVC++ 5.0";
198
    break;
199
  default:
200
#if ( _MSC_VER > 1800 )
201
    result = "MSVC++ newer than version 12.0";
202
#elif ( _MSC_VER < 1100 )
203
    result = "MSVC++ older than version 5.0";
204
#else
205
    result = "MSVC++ (some unsupported version)";
206
#endif
207
    break;
208
  }
209
#endif
210

211
  return result;
×
212
}
213

214
void doHttpPOST( const string& host, const string& url, const string& content )
×
215
{
216
#define MAXLINE 4096
217
  char request[MAXLINE + 1];
218
  SOCKET socketFD;
219
  char targetIP[INET6_ADDRSTRLEN];
220

221
/**
222
 * prepare the request
223
 */
224
#ifdef _MSC_VER
225
  _snprintf(
226
#else
227
  snprintf(
×
228
#endif
229
      request, MAXLINE,
230
      "POST %s HTTP/1.0\r\n"
231
      "Host: %s\r\n"
232
      "Content-Type: application/x-www-form-urlencoded\r\n"
233
      "User-Agent: POL in-app abort reporting system, %s\r\n"
234
      "Content-length: %d\r\n\r\n"
235
      "%s",
236
      url.c_str(), host.c_str(), POL_VERSION_ID, (int)content.size(), content.c_str() );
×
237

238
  /**
239
   * DNS lookup if needed
240
   */
241
  struct addrinfo* serverAddr;
242
  struct addrinfo hints;
243
  memset( &hints, 0, sizeof hints );
×
244
  hints.ai_family = AF_UNSPEC;  // IPv4 or IPv6
×
245
  hints.ai_flags = AI_ADDRCONFIG;
×
246
  hints.ai_socktype = SOCK_STREAM;
×
247
  int res = getaddrinfo( host.c_str(), "http", &hints, &serverAddr );
×
248
  if ( res != 0 )
×
249
  {
250
    fprintf( stderr, "getaddrinfo() failed for \"%s\" due to \"%s\"(code: %d)\n", host.c_str(),
×
251
             gai_strerror( res ), res );
252
    std::_Exit( 1 );
×
253
  }
254

255
  switch ( serverAddr->ai_addr->sa_family )
×
256
  {
257
  case AF_INET:
×
258
    if ( inet_ntop( AF_INET, &( (struct sockaddr_in*)serverAddr->ai_addr )->sin_addr, targetIP,
×
259
                    INET_ADDRSTRLEN ) == nullptr )
×
260
      std::_Exit( 1 );
×
261
    break;
×
262

263
  case AF_INET6:
×
264
    if ( inet_ntop( AF_INET6, &( (struct sockaddr_in*)serverAddr->ai_addr )->sin_addr, targetIP,
×
265
                    INET6_ADDRSTRLEN ) == nullptr )
×
266
      std::_Exit( 1 );
×
267
    break;
×
268

269
  default:
×
270
    fprintf( stderr, "Unknown address family found for %s\n", host.c_str() );
×
271
    std::_Exit( 1 );
×
272
  }
273

274
  // create the socket
275
  socketFD = socket( serverAddr->ai_family, serverAddr->ai_socktype, serverAddr->ai_protocol );
×
276

277
  /**
278
   * connect to the bug tracking server
279
   */
280
  if ( ( res = connect( socketFD, serverAddr->ai_addr, (int)serverAddr->ai_addrlen ) ) != 0 )
×
281
  {
282
    fprintf( stderr, "connect() failed for server \"%s\"(IP: %s) due \"%s\"(%d)\n", host.c_str(),
×
283
             targetIP, strerror( errno ), errno );
×
284
    std::_Exit( 1 );
×
285
  }
286

287
  freeaddrinfo( serverAddr );  // not needed anymore
×
288

289
/**
290
 * send the request
291
 */
292
#if !defined( _WIN32 ) && !defined( __APPLE__ )
293
  send( socketFD, request, strlen( request ), MSG_NOSIGNAL );
×
294
#else
295
  send( socketFD, request, (int)strlen( request ), 0 );
296
#endif
297
  printf( "Abort report was sent to %s%s (IP: %s)\n", host.c_str(), url.c_str(), targetIP );
×
298

299
  /**
300
   * wait for some answers and print them on the screen
301
   */
302
  ssize_t readBytes;
303
  char answer[MAXLINE + 1];
304

305
#if !defined( _WIN32 ) && !defined( __APPLE__ )
306
  while ( ( readBytes = recv( socketFD, answer, MAXLINE, MSG_NOSIGNAL ) ) > 0 )
×
307
  {
308
#else
309
  while ( ( readBytes = recv( socketFD, answer, MAXLINE, 0 ) ) > 0 )
310
  {
311
#endif
312
    answer[readBytes] = '\0';
×
313
    printf( "Answer from bug tracking server:\n%s\n", answer );
×
314
    // skip the received answer and proceed
315
  }
316

317
// close the socket to the bug tracking server
318
#ifndef _WIN32
319
  close( socketFD );
×
320
#else
321
  closesocket( socketFD );
322
#endif
323
}
×
324
}  // namespace
325

326
void ExceptionParser::reportProgramAbort( const string& stackTrace, const string& reason )
×
327
{
328
  /**
329
   * set some default values if the abort occurs too early and pol.cfg wasn't parsed yet
330
   */
331
  string host = "polserver.com";
×
332
  string url = "/pol/report_program_abort.php";
×
333
  if ( !m_programAbortReportingServer.empty() )
×
334
  {
335
    host = m_programAbortReportingServer;
×
336
    if ( !m_programAbortReportingUrl.empty() )
×
337
      url = m_programAbortReportingUrl;
×
338
  }
339

340
  // create the abort description for the subsequent POST request
341
  string content = "email=" + m_programAbortReportingReporter +
×
342
                   "&"
343
                   "bin=" +
×
344
                   PROG_CONFIG::programName() +
×
345
                   "&"
346
                   "start_time=" +
×
347
                   m_programStart +
×
348
                   "&"
349
                   "abort_time=" +
×
350
                   Pol::Clib::Logging::LogSink::getTimeStamp() +
×
351
                   "&"
352
                   "reason=" +
×
353
                   reason +
×
354
                   "&"
355
                   "trace=" +
×
356
                   stackTrace +
×
357
                   "&"
358
                   "comp=" +
×
359
                   getCompilerVersion() +
×
360
                   "&"
361
                   "comp_time=" +
×
362
                   ProgramConfig::build_datetime() +
×
363
                   "&"
364
                   "build_target=" +
×
365
                   ProgramConfig::build_target() +
×
366
                   "&"
367
                   "build_revision=" POL_VERSION_ID
368
                   "&"
369
                   "misc=";
×
370

371
  // execute the POST request
372
  doHttpPOST( host, url, content );
×
373
}
×
374

375
namespace
376
{
377
// The script this thread was last known to be running, for a report to print beside its
378
// native trace. Taken with the non-blocking snapshot: a report that waits on a lock held by
379
// the very thread it describes has traded the report for a hang.
380
//
381
// "Last" is meant literally -- nothing clears the value when a script finishes, so this can
382
// name a script the thread has already moved on from. Labelled accordingly, matching the
383
// minidump and the watchdog dump.
NEW
384
std::string last_script_line()
×
385
{
NEW
386
  std::string name;
×
387
  unsigned pc;
NEW
388
  Clib::script_status.snapshot( name, pc );
×
NEW
389
  return fmt::format( "Last Script: {} PC: {}", name, pc );
×
NEW
390
}
×
391
}  // namespace
392

393
void ExceptionParser::registerWorldLockOwner( const std::atomic<size_t>* owner )
4 ✔
394
{
395
  m_worldLockOwner.store( owner, std::memory_order_relaxed );
4 ✔
396
}
4 ✔
397

UNCOV
398
void ExceptionParser::handleExceptionSignal( int signal )
×
399
{
400
  switch ( signal )
×
401
  {
402
  case SIGILL:
×
403
  case SIGFPE:
404
  case SIGSEGV:
405
  case SIGTERM:
406
  case SIGABRT:
407
  {
408
    /**
409
     * inform the user about the program abort
410
     */
411
    printf(
×
412
        "########################################################################################"
413
        "\n" );
414
    if ( m_programAbortReporting )
×
415
      printf( "POL will exit now. The following will be sent to the POL developers:\n\n" );
×
416
    else
417
      printf(
×
418
          "POL will exit now. Please, post the following to the forum: "
419
          "http://forums.polserver.com/.\n" );
420
    // Everything cheap goes out and is flushed before the stack trace is walked. That walk
421
    // symbolizes and allocates, and the crashes where it matters most -- a corrupted heap, a
422
    // fault taken inside the allocator -- are exactly the ones where it hangs or faults again.
423
    // Gathering it first, as this did, meant those crashes printed nothing at all.
424
    //
425
    // Which thread died is the first thing anyone reading this needs, and the trace alone
426
    // rarely says: the top frames are usually library code shared by every thread. Name comes
427
    // from the per-thread buffer, so no lock is taken here. SIGSEGV and SIGABRT are both
428
    // delivered to the thread that caused them, so this is the faulting thread and not a
429
    // bystander.
430
    printf( "Thread: %s (%zu)\n", threadhelp::current_thread_name(), threadhelp::thread_pid() );
×
NEW
431
    printf( "%s\n", last_script_line().c_str() );
×
NEW
432
    const std::atomic<size_t>* owner_slot = m_worldLockOwner.load( std::memory_order_relaxed );
×
NEW
433
    if ( owner_slot != nullptr )
×
434
    {
NEW
435
      const size_t owner = owner_slot->load( std::memory_order_relaxed );
×
NEW
436
      if ( owner != 0 )
×
NEW
437
        printf( "World lock held by tid: %zu\n", owner );
×
438
      else
NEW
439
        printf( "World lock: free\n" );
×
440
    }
441
    printf( "Admin contact: %s\n", m_programAbortReportingReporter.c_str() );
×
442
    printf( "Executable: %s\n", PROG_CONFIG::programName().c_str() );
×
443
    printf( "Start time: %s\n", m_programStart.c_str() );
×
444
    printf( "Current time: %s\n", Pol::Clib::Logging::LogSink::getTimeStamp().c_str() );
×
445
    printf( "\n" );
×
NEW
446
    fflush( stdout );
×
447

NEW
448
    string tStackTrace = ExceptionParser::getTrace();
×
449
    printf( "Stack trace:\n%s", tStackTrace.c_str() );
×
450
    printf( "\n" );
×
451
    printf( "Compiler: %s\n", getCompilerVersion().c_str() );
×
452
    printf( "Compile time: %s\n", ProgramConfig::build_datetime().c_str() );
×
453
    printf( "Build target: %s\n", ProgramConfig::build_target().c_str() );
×
454
    printf( "Build revision: %s\n", POL_VERSION_ID );
×
455
#if defined( __GLIBC__ )
456
    printf( "GNU C library (compile time): %d.%d\n", __GLIBC__, __GLIBC_MINOR__ );
×
457
#endif
458
    printf( "\n" );
×
459
    printf(
×
460
        "########################################################################################"
461
        "\n" );
462
    fflush( stdout );
×
463

464
    /**
465
     * use the program abort reporting system
466
     */
467
    if ( m_programAbortReporting )
×
468
    {
469
      string signalName;
×
470
      string signalDescription;
×
471

472
      getSignalDescription( signal, signalName, signalDescription );
×
473
      ExceptionParser::reportProgramAbort(
×
474
          tStackTrace, "CRASH caused by signal " + signalName + " (" + signalDescription +
×
475
                           ") in thread \"" + threadhelp::current_thread_name() + "\"" );
×
476
    }
×
477

478
    // finally, go to hell
479
    std::_Exit( 1 );
×
480
  }
×
481
  break;
482
  default:
×
483
    break;
×
484
  }
485
}
×
486

487
///////////////////////////////////////////////////////////////////////////////
488

489
ExceptionParser::ExceptionParser() = default;
×
490

491
ExceptionParser::~ExceptionParser() {}
×
492

493
///////////////////////////////////////////////////////////////////////////////
494

495
#if !defined( _WIN32 ) && !defined( __APPLE__ )
496
static void handleSignalLinux( int signal, siginfo_t* signalInfo, void* arg )
×
497
{
498
  (void)arg;
499
  logExceptionSignal( signal );
×
500
  if ( signalInfo != nullptr )
×
501
  {
502
    if ( signal == SIGSEGV )
×
503
    {
504
      if ( signalInfo->si_addr != nullptr )
×
505
        printf( "Segmentation fault detected - faulty memory reference at location: %p\n",
×
506
                signalInfo->si_addr );
507
      else
508
        printf( "Segmentation fault detected - null pointer reference\n" );
×
509
    }
510
    if ( signalInfo->si_errno != 0 )
×
511
      printf( "This signal occurred because \"%s\"(%d)\n", strerror( signalInfo->si_errno ),
×
512
              signalInfo->si_errno );
513
    if ( signalInfo->si_code != 0 )
×
514
      printf( "Signal code is %d\n", signalInfo->si_code );
×
515
  }
516
  ExceptionParser::handleExceptionSignal( signal );
×
517
}
×
518

519
static void handleStackTraceRequestLinux( int signal, siginfo_t* signalInfo, void* arg )
×
520
{
521
  (void)signal;
522
  (void)signalInfo;
523
  (void)arg;
524
  // Name from the per-thread buffer rather than the ThreadMap: this runs in a
525
  // signal handler, and logAllStackTraces() below raises SIGUSR1 on every thread
526
  // at once, so each of them would be contending for the map's spinlock -- and
527
  // deadlocking outright against a thread stopped while holding it.
528
  std::string output =
NEW
529
      fmt::format( "STACK TRACE for thread \"{}\"({}):\n{}\n", threadhelp::current_thread_name(),
×
NEW
530
                   threadhelp::thread_pid(), last_script_line() );
×
UNCOV
531
  output += ExceptionParser::getTrace() + '\n';
×
532

533
  // print to stdout
534
  printf( "%s", output.c_str() );
×
535

536
  // print to error output
537
  POLLOG_ERROR( output );
×
538

539
  // wait here for logging facility to make sure everything was processed
540
  if ( Clib::Logging::global_logger )
×
541
    Clib::Logging::global_logger->wait_for_empty_queue();
×
542
}
×
543

544
void ExceptionParser::logAllStackTraces()
×
545
{
546
  threadhelp::ThreadMap::Contents threadsDesc;
×
547
  threadhelp::threadmap_instance().CopyContents( threadsDesc );
×
548
  for ( const auto& threadDesc : threadsDesc )
×
549
  {
550
    pthread_t threadID = (pthread_t)threadDesc.first;
×
551

552
    if ( pthread_kill( threadID, SIGUSR1 ) != 0 )
×
553
    {
554
      fprintf( stderr, "%s",
×
555
               fmt::format( "pthread_kill() failed to send SIGUSR1 to thread {}({})\n",
×
556
                            threadsDesc[threadID], threadID )
×
557
                   .c_str() );
558
    }
559
  }
560
}
×
561

562
void ExceptionParser::initGlobalExceptionCatching()
5,261 ✔
563
{
564
  struct sigaction sigAction;
565

566
  memset( &sigAction, 0, sizeof( sigAction ) );
5,261 ✔
567
  sigemptyset( &sigAction.sa_mask );
5,261 ✔
568
  sigAction.sa_sigaction = handleSignalLinux;
5,261 ✔
569
  sigAction.sa_flags = SA_SIGINFO;
5,261 ✔
570
  sigaction( SIGINT, &sigAction, nullptr );
5,261 ✔
571
  sigaction( SIGTERM, &sigAction, nullptr );
5,261 ✔
572
  sigaction( SIGSEGV, &sigAction, nullptr );
5,261 ✔
573
  sigaction( SIGABRT, &sigAction, nullptr );
5,261 ✔
574
  sigAction.sa_sigaction = handleStackTraceRequestLinux;
5,261 ✔
575
  sigaction( SIGUSR1, &sigAction, nullptr );
5,261 ✔
576

577
  // set handler stack
578
  stack_t tStack;
579
  // mmap: no false positives for leak, plus guardpages to get SIGSEGV on memory overwrites
580
  char* mem = static_cast<char*>( mmap( nullptr, SIGSTKSZ + 2 * getpagesize(),
5,261 ✔
581
                                        PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0 ) );
582
  mprotect( mem, getpagesize(), PROT_NONE );
5,261 ✔
583
  mprotect( mem + getpagesize() + SIGSTKSZ, getpagesize(), PROT_NONE );
5,261 ✔
584
  tStack.ss_sp = mem + getpagesize();
5,261 ✔
585
  tStack.ss_size = SIGSTKSZ;
5,261 ✔
586
  tStack.ss_flags = 0;
5,261 ✔
587
  if ( sigaltstack( &tStack, nullptr ) == -1 )
5,261 ✔
588
  {
589
    printf( "Could not set signal handler stack\n" );
×
590
    std::exit( 1 );
×
591
  }
592
}
5,261 ✔
593
#else  // _WIN32 || Apple
594

595
void ExceptionParser::logAllStackTraces() {}
596

597
void ExceptionParser::initGlobalExceptionCatching()
598
{
599
#if defined( _WIN32 )
600
  // see compile_defs.cmake
601
  // in addition add the executable path as searchpath
602
  wchar_t path[MAX_PATH];
603
  GetModuleFileNameW( nullptr, path, MAX_PATH );
604
  PathRemoveFileSpecW( path );
605
  SetEnvironmentVariableW( L"_NT_ALT_SYMBOL_PATH", path );
606
#endif
607
}
608
#endif  // _WIN32 || Apple
609

610
string ExceptionParser::getTrace()
4 ✔
611
{
612
  auto stack = boost::stacktrace::stacktrace::from_current_exception();
4 ✔
613
  // current_exception does not always work, eg no active exception
614
  // and needs linking with libboost_stacktrace_backtrace, which as of now is not possible with
615
  // Apple. Current stacktrace as fallback, which should give enough infos
616
  if ( stack.empty() )
4 ✔
617
    stack = boost::stacktrace::stacktrace();
2 ✔
618
  return boost::stacktrace::to_string( stack );
8 ✔
619
}
4 ✔
620

621
std::string indent_stack_block( const std::string& block, size_t max_lines )
39 ✔
622
{
623
  std::string_view remaining( block );
39 ✔
624
  while ( !remaining.empty() && ( remaining.back() == '\n' || remaining.back() == '\r' ) )
54 ✔
625
    remaining.remove_suffix( 1 );
15 ✔
626

627
  std::string result;
39 ✔
628
  size_t kept = 0, dropped = 0;
39 ✔
629

630
  while ( !remaining.empty() )
810 ✔
631
  {
632
    auto eol = remaining.find( '\n' );
771 ✔
633
    auto line = remaining.substr( 0, eol );
771 ✔
634
    remaining = eol == std::string_view::npos ? std::string_view() : remaining.substr( eol + 1 );
771 ✔
635

636
    if ( kept < max_lines )
771 ✔
637
    {
638
      fmt::format_to( std::back_inserter( result ), "  {}\n", line );
277 ✔
639
      ++kept;
277 ✔
640
    }
641
    else
642
      ++dropped;
494 ✔
643
  }
644

645
  if ( dropped )
39 ✔
646
    fmt::format_to( std::back_inserter( result ), "  ... ({} more)\n", dropped );
6 ✔
647

648
  return result;
78 ✔
649
}
×
650

651
void ExceptionParser::configureProgramAbortReportingSystem( bool active, std::string server,
56 ✔
652
                                                            std::string url, std::string reporter )
653
{
654
  m_programAbortReporting = active;
56 ✔
655
  m_programAbortReportingServer = std::move( server );
56 ✔
656
  m_programAbortReportingUrl = std::move( url );
56 ✔
657
  m_programAbortReportingReporter = std::move( reporter );
56 ✔
658
}
56 ✔
659

660
bool ExceptionParser::programAbortReporting()
2 ✔
661
{
662
  return m_programAbortReporting;
2 ✔
663
}
664

665
///////////////////////////////////////////////////////////////////////////////
666
}  // namespace Pol::Clib
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc