System wide sampling like 'perf top' or 'perf record -a' read all threads /proc/xxx/maps before sampling. If there are any threads which generating a keeping growing huge maps, perf will do infinite loop during synthesizing. Nothing will be sampled. This patch fixes this issue by adding per-thread timeout to force stop this kind of endless proc map processing. PERF_RECORD_MISC_PROC_MAP_PARSE_TIME_OUT is introduced to indicate that the mmap record are truncated by time out. User will get warning notification when truncated mmap records are detected.
Unified · Split
| @@ -565,6 +565,10 @@ struct perf_event_mmap_page { · 564 unmodified lines | ||
| 565 | 565 | #define PERF_RECORD_MISC_GUEST_KERNEL (4 << 0) |
| 566 | 566 | #define PERF_RECORD_MISC_GUEST_USER (5 << 0) |
| 567 | 567 | |
| 568 | +/* | |
| 569 | + * Indicates that /proc/PID/maps parsing are truncated by time out. | |
| 570 | + */ | |
| 571 | +#define PERF_RECORD_MISC_PROC_MAP_PARSE_TIMEOUT (1 << 12) | |
| 568 | 572 | /* |
| 569 | 573 | * PERF_RECORD_MISC_MMAP_DATA and PERF_RECORD_MISC_COMM_EXEC are used on |
| 570 | 574 | * different events so can reuse the same bit position. |
| @@ -213,6 +213,8 @@ static int perf_event__synthesize_fork(struct perf_tool *tool, · 212 unmodified lines | ||
| 213 | 213 | return 0; |
| 214 | 214 | } |
| 215 | 215 | |
| 216 | +#define PROC_MAP_PARSE_TIMEOUT (500 * 1000000ULL) | |
| 217 | + | |
| 216 | 218 | int perf_event__synthesize_mmap_events(struct perf_tool *tool, |
| 217 | 219 | union perf_event *event, |
| 218 | 220 | pid_t pid, pid_t tgid, |
| @@ -222,6 +224,8 @@ int perf_event__synthesize_mmap_events(struct perf_tool *tool, · 3 unmodified lines | ||
| 222 | 224 | { |
| 223 | 225 | char filename[PATH_MAX]; |
| 224 | 226 | FILE *fp; |
| 227 | + unsigned long long t; | |
| 228 | + bool truncation = false; | |
| 225 | 229 | int rc = 0; |
| 226 | 230 | |
| 227 | 231 | if (machine__is_default_guest(machine)) |
| @@ -240,6 +244,7 @@ int perf_event__synthesize_mmap_events(struct perf_tool *tool, · 12 unmodified lines | ||
| 240 | 244 | } |
| 241 | 245 | |
| 242 | 246 | event->header.type = PERF_RECORD_MMAP2; |
| 247 | + t = rdclock(); | |
| 243 | 248 | |
| 244 | 249 | while (1) { |
| 245 | 250 | char bf[BUFSIZ]; |
| @@ -253,6 +258,12 @@ int perf_event__synthesize_mmap_events(struct perf_tool *tool, · 7 unmodified lines | ||
| 253 | 258 | if (fgets(bf, sizeof(bf), fp) == NULL) |
| 254 | 259 | break; |
| 255 | 260 | |
| 261 | + if ((rdclock() - t) > PROC_MAP_PARSE_TIMEOUT) { | |
| 262 | + pr_warning("Reading %s time out.\n", filename); | |
| 263 | + truncation = true; | |
| 264 | + goto out; | |
| 265 | + } | |
| 266 | + | |
| 256 | 267 | /* ensure null termination since stack will be reused. */ |
| 257 | 268 | strcpy(execname, ""); |
| 258 | 269 | |
| @@ -301,6 +312,10 @@ int perf_event__synthesize_mmap_events(struct perf_tool *tool, · 42 unmodified lines | ||
| 301 | 312 | event->header.misc |= PERF_RECORD_MISC_MMAP_DATA; |
| 302 | 313 | } |
| 303 | 314 | |
| 315 | +out: | |
| 316 | + if (truncation) | |
| 317 | + event->header.misc |= PERF_RECORD_MISC_PROC_MAP_PARSE_TIMEOUT; | |
| 318 | + | |
| 304 | 319 | if (!strcmp(execname, "")) |
| 305 | 320 | strcpy(execname, anonstr); |
| 306 | 321 | |
| @@ -319,6 +334,9 @@ int perf_event__synthesize_mmap_events(struct perf_tool *tool, · 12 unmodified lines | ||
| 319 | 334 | rc = -1; |
| 320 | 335 | break; |
| 321 | 336 | } |
| 337 | + | |
| 338 | + if (truncation) | |
| 339 | + break; | |
| 322 | 340 | } |
| 323 | 341 | |
| 324 | 342 | fclose(fp); |
| @@ -265,6 +265,7 @@ struct events_stats { · 264 unmodified lines | ||
| 265 | 265 | u32 nr_unknown_id; |
| 266 | 266 | u32 nr_unprocessable_samples; |
| 267 | 267 | u32 nr_auxtrace_errors[PERF_AUXTRACE_ERROR_MAX]; |
| 268 | + u32 nr_proc_map_timeout; | |
| 268 | 269 | }; |
| 269 | 270 | |
| 270 | 271 | struct attr_event { |
| @@ -1064,6 +1064,8 @@ static int machines__deliver_event(struct machines *machines, · 1063 unmodified lines | ||
| 1064 | 1064 | case PERF_RECORD_MMAP: |
| 1065 | 1065 | return tool->mmap(tool, event, sample, machine); |
| 1066 | 1066 | case PERF_RECORD_MMAP2: |
| 1067 | + if (event->header.misc & PERF_RECORD_MISC_PROC_MAP_PARSE_TIMEOUT) | |
| 1068 | + ++evlist->stats.nr_proc_map_timeout; | |
| 1067 | 1069 | return tool->mmap2(tool, event, sample, machine); |
| 1068 | 1070 | case PERF_RECORD_COMM: |
| 1069 | 1071 | return tool->comm(tool, event, sample, machine); |
| @@ -1360,6 +1362,15 @@ static void perf_session__warn_about_errors(const struct perf_session *session) · 290 unmodified lines | ||
| 1360 | 1362 | ui__warning("%u out of order events recorded.\n", oe->nr_unordered_events); |
| 1361 | 1363 | |
| 1362 | 1364 | events_stats__auxtrace_error_warn(stats); |
| 1365 | + | |
| 1366 | + if (stats->nr_proc_map_timeout != 0) { | |
| 1367 | + ui__warning("%d map information files for pre-existing threads were\n" | |
| 1368 | + "not processed, if there are samples for addresses they\n" | |
| 1369 | + "will not be resolved, you may find out which are these\n" | |
| 1370 | + "threads by running with -v and redirecting the output\n" | |
| 1371 | + "to a file.\n", | |
| 1372 | + stats->nr_proc_map_timeout); | |
| 1373 | + } | |
| 1363 | 1374 | } |
| 1364 | 1375 | |
| 1365 | 1376 | static int perf_session__flush_thread_stack(struct thread *thread, |