Skip to content
This repository was archived by the owner on Mar 28, 2020. It is now read-only.

Commit 2cf73dc

Browse files
committed
[profile] Add support for the exit-on-signal '%Nx' specifier
The exit-on-signal specifier tells the runtime to handle a signal by writing out a profile and then exiting the program. It can be specified in the LLVM_PROFILE_FILE environment variable or by calling __llvm_profile_set_filename. Here is how you might force a program to exit when it's sent SIGTERM (sig 15): LLVM_PROFILE_FILE="default.profraw%15x" <program> When an instance of <program> is sent SIGTERM, it will write out a profile to "default.profraw" (the specifier is stripped out of the filename) and exit with return code 0. It's possible to use multiple exit-on-signal specifiers (up to 16). rdar://problem/24098975
1 parent 1264927 commit 2cf73dc

File tree

2 files changed

+81
-5
lines changed

2 files changed

+81
-5
lines changed

lib/profile/InstrProfilingFile.c

Lines changed: 65 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include <stdio.h>
1515
#include <stdlib.h>
1616
#include <string.h>
17+
#include <signal.h>
1718
#ifdef _MSC_VER
1819
/* For _alloca. */
1920
#include <malloc.h>
@@ -59,6 +60,7 @@ static const char *getPNSStr(ProfileNameSpecifier PNS) {
5960
}
6061

6162
#define MAX_PID_SIZE 16
63+
#define MAX_SIGNAL_HANDLERS 16
6264
/* Data structure holding the result of parsed filename pattern. */
6365
typedef struct lprofFilename {
6466
/* File name string possibly with %p or %h specifiers. */
@@ -79,11 +81,13 @@ typedef struct lprofFilename {
7981
* 2 profile data files. %1m is equivalent to %m. Also %m specifier
8082
* can only appear once at the end of the name pattern. */
8183
unsigned MergePoolSize;
84+
char ExitOnSignals[MAX_SIGNAL_HANDLERS];
85+
unsigned NumExitSignals;
8286
ProfileNameSpecifier PNS;
8387
} lprofFilename;
8488

85-
COMPILER_RT_WEAK lprofFilename lprofCurFilename = {0, 0, 0, {0}, {0},
86-
0, 0, 0, PNS_unknown};
89+
COMPILER_RT_WEAK lprofFilename lprofCurFilename = {
90+
0, 0, 0, {0}, {0}, 0, 0, 0, {0}, 0, PNS_unknown};
8791

8892
int getpid(void);
8993
static int getCurFilenameLength();
@@ -252,6 +256,26 @@ static void truncateCurrentFile(void) {
252256
fclose(File);
253257
}
254258

259+
static void exitSignalHandler(int sig) {
260+
(void)sig;
261+
exit(0);
262+
}
263+
264+
static void installExitSignalHandlers(void) {
265+
unsigned I;
266+
struct sigaction sigact;
267+
int err;
268+
for (I = 0; I < lprofCurFilename.NumExitSignals; ++I) {
269+
memset(&sigact, 0, sizeof(sigact));
270+
sigact.sa_handler = exitSignalHandler;
271+
err = sigaction(lprofCurFilename.ExitOnSignals[I], &sigact, NULL);
272+
if (err)
273+
PROF_WARN(
274+
"Unable to install an exit signal handler for %d (errno = %d).\n",
275+
lprofCurFilename.ExitOnSignals[I], err);
276+
}
277+
}
278+
255279
static const char *DefaultProfileName = "default.profraw";
256280
static void resetFilenameToDefault(void) {
257281
if (lprofCurFilename.FilenamePat && lprofCurFilename.OwnsFilenamePat) {
@@ -262,14 +286,23 @@ static void resetFilenameToDefault(void) {
262286
lprofCurFilename.PNS = PNS_default;
263287
}
264288

289+
static int isNonZeroDigit(char C) { return C >= '1' && C <= '9'; }
290+
265291
static int containsMergeSpecifier(const char *FilenamePat, int I) {
266292
return (FilenamePat[I] == 'm' ||
267-
(FilenamePat[I] >= '1' && FilenamePat[I] <= '9' &&
293+
(isNonZeroDigit(FilenamePat[I]) &&
268294
/* If FilenamePat[I] is not '\0', the next byte is guaranteed
269295
* to be in-bound as the string is null terminated. */
270296
FilenamePat[I + 1] == 'm'));
271297
}
272298

299+
static int containsExitOnSignalSpecifier(const char *FilenamePat, int I) {
300+
if (!isNonZeroDigit(FilenamePat[I]))
301+
return 0;
302+
return (FilenamePat[I + 1] == 'x') ||
303+
(isNonZeroDigit(FilenamePat[I + 1]) && FilenamePat[I + 2] == 'x');
304+
}
305+
273306
/* Parses the pattern string \p FilenamePat and stores the result to
274307
* lprofcurFilename structure. */
275308
static int parseFilenamePattern(const char *FilenamePat,
@@ -278,6 +311,7 @@ static int parseFilenamePattern(const char *FilenamePat,
278311
char *PidChars = &lprofCurFilename.PidChars[0];
279312
char *Hostname = &lprofCurFilename.Hostname[0];
280313
int MergingEnabled = 0;
314+
char SignalNo;
281315

282316
/* Clean up cached prefix. */
283317
if (lprofCurFilename.ProfilePathPrefix)
@@ -327,6 +361,22 @@ static int parseFilenamePattern(const char *FilenamePat,
327361
lprofCurFilename.MergePoolSize = FilenamePat[I] - '0';
328362
I++; /* advance to 'm' */
329363
}
364+
} else if (containsExitOnSignalSpecifier(FilenamePat, I)) {
365+
if (lprofCurFilename.NumExitSignals == MAX_SIGNAL_HANDLERS) {
366+
PROF_WARN("%%x specifier has been specified too many times in %s.\n",
367+
FilenamePat);
368+
return -1;
369+
}
370+
/* Grab the signal number. */
371+
SignalNo = FilenamePat[I] - '0';
372+
I++; /* advance to either another digit, or 'x' */
373+
if (FilenamePat[I] != 'x') {
374+
SignalNo = (SignalNo * 10) + (FilenamePat[I] - '0');
375+
I++; /* advance to 'x' */
376+
}
377+
lprofCurFilename.ExitOnSignals[lprofCurFilename.NumExitSignals] =
378+
SignalNo;
379+
++lprofCurFilename.NumExitSignals;
330380
}
331381
}
332382

@@ -370,6 +420,7 @@ static void parseAndSetFilename(const char *FilenamePat,
370420
}
371421

372422
truncateCurrentFile();
423+
installExitSignalHandlers();
373424
}
374425

375426
/* Return buffer length that is required to store the current profile
@@ -378,18 +429,24 @@ static void parseAndSetFilename(const char *FilenamePat,
378429
#define SIGLEN 24
379430
static int getCurFilenameLength() {
380431
int Len;
432+
unsigned I;
381433
if (!lprofCurFilename.FilenamePat || !lprofCurFilename.FilenamePat[0])
382434
return 0;
383435

384436
if (!(lprofCurFilename.NumPids || lprofCurFilename.NumHosts ||
385-
lprofCurFilename.MergePoolSize))
437+
lprofCurFilename.MergePoolSize || lprofCurFilename.NumExitSignals))
386438
return strlen(lprofCurFilename.FilenamePat);
387439

388440
Len = strlen(lprofCurFilename.FilenamePat) +
389441
lprofCurFilename.NumPids * (strlen(lprofCurFilename.PidChars) - 2) +
390442
lprofCurFilename.NumHosts * (strlen(lprofCurFilename.Hostname) - 2);
391443
if (lprofCurFilename.MergePoolSize)
392444
Len += SIGLEN;
445+
for (I = 0; I < lprofCurFilename.NumExitSignals; ++I) {
446+
Len -= 3; /* Drop the '%', signal number, and the 'x'. */
447+
if (lprofCurFilename.ExitOnSignals[I] >= 10)
448+
--Len; /* Drop the second digit of the signal number. */
449+
}
393450
return Len;
394451
}
395452

@@ -405,7 +462,7 @@ static const char *getCurFilename(char *FilenameBuf) {
405462
return 0;
406463

407464
if (!(lprofCurFilename.NumPids || lprofCurFilename.NumHosts ||
408-
lprofCurFilename.MergePoolSize))
465+
lprofCurFilename.MergePoolSize || lprofCurFilename.NumExitSignals))
409466
return lprofCurFilename.FilenamePat;
410467

411468
PidLength = strlen(lprofCurFilename.PidChars);
@@ -431,6 +488,9 @@ static const char *getCurFilename(char *FilenameBuf) {
431488
J += S;
432489
if (FilenamePat[I] != 'm')
433490
I++;
491+
} else if (containsExitOnSignalSpecifier(FilenamePat, I)) {
492+
while (FilenamePat[I] != 'x')
493+
++I;
434494
}
435495
/* Drop any unknown substitutions. */
436496
} else
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// RUN: %clang_profgen -o %t %s
2+
// RUN: %run LLVM_PROFILE_FILE="%15x%t.profraw" %t
3+
// RUN: llvm-profdata show --all-functions %t.profraw | FileCheck %s
4+
// CHECK: Total functions: 1
5+
6+
#include <signal.h>
7+
#include <unistd.h>
8+
9+
int main() {
10+
kill(getpid(), SIGTERM);
11+
12+
while (1) {
13+
/* loop forever */
14+
}
15+
return 1;
16+
}

0 commit comments

Comments
 (0)