Skip to content

Commit 5301f4c

Browse files
authored
[Sanitizer] Use % patterns in report paths (#141820)
Support `%` patterns in sanitizer report paths, similar to the patterns supported in IRPGO https://github.com/llvm/llvm-project/blob/4bf67cdf026478c4bc3e708153c02f82e2c22524/compiler-rt/lib/profile/InstrProfilingFile.c#L999-L1017 * `%%` becomes `%` * `%H` expands to the environment variable `HOME` * `%t` expands to the environment variable `TMPDIR` * `%p` expands to the process ID (PID) In particular, the `%H` pattern is useful to resolve the home directory at runtime, which is not possible before this PR. Also, avoid using `Report()` before the report path has been set.
1 parent e4ef651 commit 5301f4c

File tree

3 files changed

+121
-18
lines changed

3 files changed

+121
-18
lines changed

compiler-rt/lib/sanitizer_common/sanitizer_file.cpp

Lines changed: 67 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -96,12 +96,76 @@ static void RecursiveCreateParentDirs(char *path) {
9696
}
9797
}
9898

99+
/// Parse the report path \p pattern and copy the parsed path to \p dest.
100+
///
101+
/// * `%%` becomes `%`
102+
/// * `%H` expands to the environment variable `HOME`
103+
/// * `%t` expands to the environment variable `TMPDIR`
104+
/// * `%p` expands to the process ID (PID)
105+
static void ParseAndSetPath(const char *pattern, char *dest,
106+
const uptr dest_size) {
107+
CHECK(pattern);
108+
CHECK(dest);
109+
CHECK_GE(dest_size, 1);
110+
dest[0] = '\0';
111+
uptr next_substr_start_idx = 0;
112+
for (uptr i = 0; i < internal_strlen(pattern) - 1; i++) {
113+
if (pattern[i] != '%')
114+
continue;
115+
int bytes_to_copy = i - next_substr_start_idx;
116+
// Copy over previous substring.
117+
CHECK_LT(internal_strlcat(dest, pattern + next_substr_start_idx,
118+
internal_strlen(dest) + bytes_to_copy + 1),
119+
dest_size);
120+
const char *str_to_concat;
121+
switch (pattern[++i]) {
122+
case '%':
123+
str_to_concat = "%";
124+
break;
125+
case 'H':
126+
str_to_concat = GetEnv("HOME");
127+
break;
128+
case 't':
129+
str_to_concat = GetEnv("TMPDIR");
130+
break;
131+
case 'p': {
132+
// Use printf directly to write the PID since it's not a static string.
133+
int remaining_capacity = dest_size - internal_strlen(dest);
134+
int bytes_copied =
135+
internal_snprintf(dest + internal_strlen(dest), remaining_capacity,
136+
"%ld", internal_getpid());
137+
CHECK_GT(bytes_copied, 0);
138+
CHECK_LT(bytes_copied, remaining_capacity);
139+
str_to_concat = "";
140+
break;
141+
}
142+
default: {
143+
// Invalid pattern: fallback to original pattern.
144+
const char *message = "ERROR: Unexpected pattern: ";
145+
WriteToFile(kStderrFd, message, internal_strlen(message));
146+
WriteToFile(kStderrFd, pattern, internal_strlen(pattern));
147+
WriteToFile(kStderrFd, "\n", internal_strlen("\n"));
148+
CHECK_LT(internal_strlcpy(dest, pattern, dest_size), dest_size);
149+
return;
150+
}
151+
}
152+
CHECK(str_to_concat);
153+
CHECK_LT(internal_strlcat(dest, str_to_concat, dest_size), dest_size);
154+
next_substr_start_idx = i + 1;
155+
}
156+
CHECK_LT(internal_strlcat(dest, pattern + next_substr_start_idx, dest_size),
157+
dest_size);
158+
}
159+
99160
void ReportFile::SetReportPath(const char *path) {
100161
if (path) {
101162
uptr len = internal_strlen(path);
102163
if (len > sizeof(path_prefix) - 100) {
103-
Report("ERROR: Path is too long: %c%c%c%c%c%c%c%c...\n", path[0], path[1],
104-
path[2], path[3], path[4], path[5], path[6], path[7]);
164+
const char *message = "ERROR: Path is too long: ";
165+
WriteToFile(kStderrFd, message, internal_strlen(message));
166+
WriteToFile(kStderrFd, path, 8);
167+
message = "...\n";
168+
WriteToFile(kStderrFd, message, internal_strlen(message));
105169
Die();
106170
}
107171
}
@@ -115,7 +179,7 @@ void ReportFile::SetReportPath(const char *path) {
115179
} else if (internal_strcmp(path, "stdout") == 0) {
116180
fd = kStdoutFd;
117181
} else {
118-
internal_snprintf(path_prefix, kMaxPathLength, "%s", path);
182+
ParseAndSetPath(path, path_prefix, kMaxPathLength);
119183
RecursiveCreateParentDirs(path_prefix);
120184
}
121185
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// RUN: %clangxx -O2 %s -o %t
2+
3+
// Case 1: Try setting a path that is an invalid/inaccessible directory.
4+
// RUN: not %env %run %t 2>&1 | FileCheck %s --check-prefix=ERROR1
5+
6+
// Case 2: Try setting a path that is too large.
7+
// RUN: not %env %run %t A 2>&1 | FileCheck %s --check-prefix=ERROR2
8+
9+
#include <sanitizer/common_interface_defs.h>
10+
#include <stdio.h>
11+
12+
int main(int argc, char **argv) {
13+
char buff[4096];
14+
if (argc == 1) {
15+
// Case 1
16+
sprintf(buff, "%s/report", argv[0]);
17+
// ERROR1: Can't create directory: {{.*}}
18+
} else {
19+
// Case 2
20+
snprintf(buff, sizeof(buff), "%04095d", 42);
21+
// ERROR2: Path is too long: 00000000...
22+
}
23+
__sanitizer_set_report_path(buff);
24+
}
Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,42 @@
11
// Test __sanitizer_set_report_path and __sanitizer_get_report_path:
22
// RUN: %clangxx -O2 %s -o %t
3-
// RUN: not %run %t 2>&1 | FileCheck %s
3+
// RUN: %env HOME=%t.homedir TMPDIR=%t.tmpdir %run %t 2>%t.err | FileCheck %s
4+
// RUN: FileCheck %s --input-file=%t.err --check-prefix=ERROR
45

5-
#include <assert.h>
66
#include <sanitizer/common_interface_defs.h>
77
#include <stdio.h>
88
#include <string.h>
99

10-
volatile int *null = 0;
11-
1210
int main(int argc, char **argv) {
13-
char buff[1000];
11+
char buff[4096];
1412
sprintf(buff, "%s.report_path/report", argv[0]);
1513
__sanitizer_set_report_path(buff);
16-
assert(strncmp(buff, __sanitizer_get_report_path(), strlen(buff)) == 0);
14+
// CHECK: {{.*}}.report_path/report.[[PID:[0-9]+]]
15+
printf("%s\n", __sanitizer_get_report_path());
1716

18-
// Try setting again with an invalid/inaccessible directory.
19-
char buff_bad[1000];
20-
sprintf(buff_bad, "%s/report", argv[0]);
21-
fprintf(stderr, "Expected bad report path: %s\n", buff_bad);
22-
// CHECK: Expected bad report path: [[BADPATH:.*]]/report
23-
__sanitizer_set_report_path(buff_bad);
24-
assert(strncmp(buff, __sanitizer_get_report_path(), strlen(buff)) == 0);
25-
}
17+
strcpy(buff, "%H/foo");
18+
__sanitizer_set_report_path(buff);
19+
// CHECK: [[T:.*]].homedir/foo.[[PID]]
20+
printf("%s\n", __sanitizer_get_report_path());
2621

27-
// CHECK: ERROR: Can't create directory: [[BADPATH]]
22+
strcpy(buff, "%t/foo");
23+
__sanitizer_set_report_path(buff);
24+
// CHECK: [[T]].tmpdir/foo.[[PID]]
25+
printf("%s\n", __sanitizer_get_report_path());
26+
27+
strcpy(buff, "%H/%p/%%foo");
28+
__sanitizer_set_report_path(buff);
29+
// CHECK: [[T]].homedir/[[PID]]/%foo.[[PID]]
30+
printf("%s\n", __sanitizer_get_report_path());
31+
32+
strcpy(buff, "%%foo%%bar");
33+
__sanitizer_set_report_path(buff);
34+
// CHECK: %foo%bar.[[PID]]
35+
printf("%s\n", __sanitizer_get_report_path());
36+
37+
strcpy(buff, "%%foo%ba%%r");
38+
__sanitizer_set_report_path(buff);
39+
// ERROR: Unexpected pattern: %%foo%ba%%r
40+
// CHECK: %%foo%ba%%r.[[PID]]
41+
printf("%s\n", __sanitizer_get_report_path());
42+
}

0 commit comments

Comments
 (0)