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

polserver / polserver / 31968403210

16 Aug 2026 07:42PM UTC coverage: 78.778% (+0.05%) from 78.729%
31968403210

push

github

web-flow
Improve stacktraces (#943)

* add log reading tests

* refactor stacktrace in script

* add a test for script exceptions

* fix tests waiting too long if POL exited too fast

* improve script stacktraces

* improve stacktraces on windows and linux

* remove windows setthreadname that only worked with debugger

* attempt at making script status thread-safe

* fix comment

158 of 197 new or added lines in 16 files covered. (80.2%)

14 existing lines in 6 files now uncovered.

57160 of 72558 relevant lines covered (78.78%)

1427142.26 hits per line

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

18.12
/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/stlutil.h"
6
#include "clib/threadhelp.h"
7
#include <pol_global_config.h>
8

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

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

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

32
#include <boost/stacktrace.hpp>
33

34
#define MAX_STACK_TRACE_DEPTH 200
35
#define MAX_STACK_TRACE_STEP_LENGTH 512
36

37

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

42
///////////////////////////////////////////////////////////////////////////////
43

44
bool ExceptionParser::m_programAbortReporting = false;
45
std::string ExceptionParser::m_programAbortReportingServer = "";
46
std::string ExceptionParser::m_programAbortReportingUrl = "";
47
std::string ExceptionParser::m_programAbortReportingReporter = "";
48
std::string ExceptionParser::m_programStart = Pol::Clib::Logging::LogSink::getTimeStamp();
49

50
///////////////////////////////////////////////////////////////////////////////
51

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

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

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

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

209
  return result;
×
210
}
211

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

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

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

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

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

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

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

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

285
  freeaddrinfo( serverAddr );  // not needed anymore
×
286

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

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

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

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

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

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

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

373
void ExceptionParser::handleExceptionSignal( int signal )
×
374
{
375
  switch ( signal )
×
376
  {
377
  case SIGILL:
×
378
  case SIGFPE:
379
  case SIGSEGV:
380
  case SIGTERM:
381
  case SIGABRT:
382
  {
383
    /**
384
     * inform the user about the program abort
385
     */
386
    printf(
×
387
        "########################################################################################"
388
        "\n" );
389
    if ( m_programAbortReporting )
×
390
      printf( "POL will exit now. The following will be sent to the POL developers:\n\n" );
×
391
    else
392
      printf(
×
393
          "POL will exit now. Please, post the following to the forum: "
394
          "http://forums.polserver.com/.\n" );
395
    string tStackTrace = ExceptionParser::getTrace();
×
396
    // Which thread died is the first thing anyone reading this needs, and the
397
    // trace alone rarely says: the top frames are usually library code shared by
398
    // every thread. Name comes from the per-thread buffer, so no lock is taken
399
    // here. SIGSEGV and SIGABRT are both delivered to the thread that caused
400
    // them, so this is the faulting thread and not a bystander.
401
    printf( "Thread: %s (%zu)\n", threadhelp::current_thread_name(), threadhelp::thread_pid() );
×
402
    printf( "Admin contact: %s\n", m_programAbortReportingReporter.c_str() );
×
403
    printf( "Executable: %s\n", PROG_CONFIG::programName().c_str() );
×
404
    printf( "Start time: %s\n", m_programStart.c_str() );
×
405
    printf( "Current time: %s\n", Pol::Clib::Logging::LogSink::getTimeStamp().c_str() );
×
406
    printf( "\n" );
×
407
    printf( "Stack trace:\n%s", tStackTrace.c_str() );
×
408
    printf( "\n" );
×
409
    printf( "Compiler: %s\n", getCompilerVersion().c_str() );
×
410
    printf( "Compile time: %s\n", ProgramConfig::build_datetime().c_str() );
×
411
    printf( "Build target: %s\n", ProgramConfig::build_target().c_str() );
×
412
    printf( "Build revision: %s\n", POL_VERSION_ID );
×
413
#if defined( __GLIBC__ )
414
    printf( "GNU C library (compile time): %d.%d\n", __GLIBC__, __GLIBC_MINOR__ );
×
415
#endif
416
    printf( "\n" );
×
417
    printf(
×
418
        "########################################################################################"
419
        "\n" );
420
    fflush( stdout );
×
421

422
    /**
423
     * use the program abort reporting system
424
     */
425
    if ( m_programAbortReporting )
×
426
    {
427
      string signalName;
×
428
      string signalDescription;
×
429

430
      getSignalDescription( signal, signalName, signalDescription );
×
431
      ExceptionParser::reportProgramAbort(
×
432
          tStackTrace, "CRASH caused by signal " + signalName + " (" + signalDescription +
×
433
                           ") in thread \"" + threadhelp::current_thread_name() + "\"" );
×
434
    }
×
435

436
    // finally, go to hell
437
    std::_Exit( 1 );
×
438
  }
×
439
  break;
440
  default:
×
441
    break;
×
442
  }
443
}
×
444

445
///////////////////////////////////////////////////////////////////////////////
446

447
ExceptionParser::ExceptionParser() = default;
×
448

449
ExceptionParser::~ExceptionParser() {}
×
450

451
///////////////////////////////////////////////////////////////////////////////
452

453
#if !defined( _WIN32 ) && !defined( __APPLE__ )
454
static void handleSignalLinux( int signal, siginfo_t* signalInfo, void* arg )
×
455
{
456
  (void)arg;
457
  logExceptionSignal( signal );
×
458
  if ( signalInfo != nullptr )
×
459
  {
460
    if ( signal == SIGSEGV )
×
461
    {
462
      if ( signalInfo->si_addr != nullptr )
×
463
        printf( "Segmentation fault detected - faulty memory reference at location: %p\n",
×
464
                signalInfo->si_addr );
465
      else
466
        printf( "Segmentation fault detected - null pointer reference\n" );
×
467
    }
468
    if ( signalInfo->si_errno != 0 )
×
469
      printf( "This signal occurred because \"%s\"(%d)\n", strerror( signalInfo->si_errno ),
×
470
              signalInfo->si_errno );
471
    if ( signalInfo->si_code != 0 )
×
472
      printf( "Signal code is %d\n", signalInfo->si_code );
×
473
  }
474
  ExceptionParser::handleExceptionSignal( signal );
×
475
}
×
476

477
static void handleStackTraceRequestLinux( int signal, siginfo_t* signalInfo, void* arg )
×
478
{
479
  (void)signal;
480
  (void)signalInfo;
481
  (void)arg;
482
  // Name from the per-thread buffer rather than the ThreadMap: this runs in a
483
  // signal handler, and logAllStackTraces() below raises SIGUSR1 on every thread
484
  // at once, so each of them would be contending for the map's spinlock -- and
485
  // deadlocking outright against a thread stopped while holding it.
486
  std::string output = fmt::format( "STACK TRACE for thread \"{}\"({}):\n",
487
                                    threadhelp::current_thread_name(), threadhelp::thread_pid() );
×
488
  output += ExceptionParser::getTrace() + '\n';
×
489

490
  // print to stdout
491
  printf( "%s", output.c_str() );
×
492

493
  // print to error output
494
  POLLOG_ERROR( output );
×
495

496
  // wait here for logging facility to make sure everything was processed
497
  if ( Clib::Logging::global_logger )
×
498
    Clib::Logging::global_logger->wait_for_empty_queue();
×
499
}
×
500

501
void ExceptionParser::logAllStackTraces()
×
502
{
503
  threadhelp::ThreadMap::Contents threadsDesc;
×
504
  threadhelp::threadmap_instance().CopyContents( threadsDesc );
×
505
  for ( const auto& threadDesc : threadsDesc )
×
506
  {
507
    pthread_t threadID = (pthread_t)threadDesc.first;
×
508

509
    if ( pthread_kill( threadID, SIGUSR1 ) != 0 )
×
510
    {
511
      fprintf( stderr, "%s",
×
512
               fmt::format( "pthread_kill() failed to send SIGUSR1 to thread {}({})\n",
×
513
                            threadsDesc[threadID], threadID )
×
514
                   .c_str() );
515
    }
516
  }
517
}
×
518

519
void ExceptionParser::initGlobalExceptionCatching()
5,261 ✔
520
{
521
  struct sigaction sigAction;
522

523
  memset( &sigAction, 0, sizeof( sigAction ) );
5,261 ✔
524
  sigemptyset( &sigAction.sa_mask );
5,261 ✔
525
  sigAction.sa_sigaction = handleSignalLinux;
5,261 ✔
526
  sigAction.sa_flags = SA_SIGINFO;
5,261 ✔
527
  sigaction( SIGINT, &sigAction, nullptr );
5,261 ✔
528
  sigaction( SIGTERM, &sigAction, nullptr );
5,261 ✔
529
  sigaction( SIGSEGV, &sigAction, nullptr );
5,261 ✔
530
  sigaction( SIGABRT, &sigAction, nullptr );
5,261 ✔
531
  sigAction.sa_sigaction = handleStackTraceRequestLinux;
5,261 ✔
532
  sigaction( SIGUSR1, &sigAction, nullptr );
5,261 ✔
533

534
  // set handler stack
535
  stack_t tStack;
536
  // mmap: no false positives for leak, plus guardpages to get SIGSEGV on memory overwrites
537
  char* mem = static_cast<char*>( mmap( nullptr, SIGSTKSZ + 2 * getpagesize(),
5,261 ✔
538
                                        PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0 ) );
539
  mprotect( mem, getpagesize(), PROT_NONE );
5,261 ✔
540
  mprotect( mem + getpagesize() + SIGSTKSZ, getpagesize(), PROT_NONE );
5,261 ✔
541
  tStack.ss_sp = mem + getpagesize();
5,261 ✔
542
  tStack.ss_size = SIGSTKSZ;
5,261 ✔
543
  tStack.ss_flags = 0;
5,261 ✔
544
  if ( sigaltstack( &tStack, nullptr ) == -1 )
5,261 ✔
545
  {
546
    printf( "Could not set signal handler stack\n" );
×
547
    std::exit( 1 );
×
548
  }
549
}
5,261 ✔
550
#else  // _WIN32 || Apple
551

552
void ExceptionParser::logAllStackTraces() {}
553

554
void ExceptionParser::initGlobalExceptionCatching()
555
{
556
#if defined( _WIN32 )
557
  // see compile_defs.cmake
558
  // in addition add the executable path as searchpath
559
  wchar_t path[MAX_PATH];
560
  GetModuleFileNameW( nullptr, path, MAX_PATH );
561
  PathRemoveFileSpecW( path );
562
  SetEnvironmentVariableW( L"_NT_ALT_SYMBOL_PATH", path );
563
#endif
564
}
565
#endif  // _WIN32 || Apple
566

567
string ExceptionParser::getTrace()
4 ✔
568
{
569
  auto stack = boost::stacktrace::stacktrace::from_current_exception();
4 ✔
570
  // current_exception does not always work, eg no active exception
571
  // and needs linking with libboost_stacktrace_backtrace, which as of now is not possible with
572
  // Apple. Current stacktrace as fallback, which should give enough infos
573
  if ( stack.empty() )
4 ✔
574
    stack = boost::stacktrace::stacktrace();
2 ✔
575
  return boost::stacktrace::to_string( stack );
8 ✔
576
}
4 ✔
577

578
std::string indent_stack_block( const std::string& block, size_t max_lines )
39 ✔
579
{
580
  std::string_view remaining( block );
39 ✔
581
  while ( !remaining.empty() && ( remaining.back() == '\n' || remaining.back() == '\r' ) )
54 ✔
582
    remaining.remove_suffix( 1 );
15 ✔
583

584
  std::string result;
39 ✔
585
  size_t kept = 0, dropped = 0;
39 ✔
586

587
  while ( !remaining.empty() )
810 ✔
588
  {
589
    auto eol = remaining.find( '\n' );
771 ✔
590
    auto line = remaining.substr( 0, eol );
771 ✔
591
    remaining = eol == std::string_view::npos ? std::string_view() : remaining.substr( eol + 1 );
771 ✔
592

593
    if ( kept < max_lines )
771 ✔
594
    {
595
      fmt::format_to( std::back_inserter( result ), "  {}\n", line );
277 ✔
596
      ++kept;
277 ✔
597
    }
598
    else
599
      ++dropped;
494 ✔
600
  }
601

602
  if ( dropped )
39 ✔
603
    fmt::format_to( std::back_inserter( result ), "  ... ({} more)\n", dropped );
6 ✔
604

605
  return result;
78 ✔
NEW
606
}
×
607

608
void ExceptionParser::configureProgramAbortReportingSystem( bool active, std::string server,
56 ✔
609
                                                            std::string url, std::string reporter )
610
{
611
  m_programAbortReporting = active;
56 ✔
612
  m_programAbortReportingServer = std::move( server );
56 ✔
613
  m_programAbortReportingUrl = std::move( url );
56 ✔
614
  m_programAbortReportingReporter = std::move( reporter );
56 ✔
615
}
56 ✔
616

617
bool ExceptionParser::programAbortReporting()
2 ✔
618
{
619
  return m_programAbortReporting;
2 ✔
620
}
621

622
///////////////////////////////////////////////////////////////////////////////
623
}  // 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