5 // Created by James McIlree on 4/19/13.
6 // Copyright (c) 2013 Apple. All rights reserved.
9 #ifndef kdprof_Printing_hpp
10 #define kdprof_Printing_hpp
12 void print_summary_header(const Globals& globals);
14 struct SummaryLineData {
16 static constexpr const char* const indent_string[] = { "", " ", " ", " " };
17 static const uint32_t MAX_INDENT_LEVEL = 3; // Need to know this for time indenting to work correctly
19 uint32_t _indent_level;
24 enum class SummaryType {
31 SummaryLineData(const char* name, uint32_t indent_level) :
32 _indent_level(indent_level),
34 should_print_timestamp(true),
37 actual_process_count(0),
38 wanted_process_count(0),
39 actual_thread_count(0),
40 wanted_thread_count(0),
41 num_vm_fault_events(0),
43 io_bytes_completed(0),
45 percent_multiplier(100.0),
46 type(SummaryType::Unknown),
51 ASSERT(_indent_level <= MAX_INDENT_LEVEL, "Sanity");
52 ASSERT(_name && strlen(_name) > 0, "Sanity");
55 bool should_print_timestamp;
57 AbsTime total_run_time;
58 AbsTime total_idle_time;
59 AbsTime total_intr_time;
60 AbsTime total_wanted_run_time;
61 AbsTime total_wallclock_run_time;
62 AbsTime total_all_cpus_idle_time;
63 AbsTime total_vm_fault_time;
64 AbsTime total_io_time;
65 AbsTime total_jetsam_time;
66 uint32_t num_intr_events;
67 uint32_t context_switches;
68 uint32_t actual_process_count;
69 uint32_t wanted_process_count;
70 uint32_t actual_thread_count;
71 uint32_t wanted_thread_count;
72 uint32_t num_vm_fault_events;
73 uint32_t num_io_events;
74 uint64_t io_bytes_completed;
75 uint32_t num_jetsam_pids;
76 double percent_multiplier;
79 const char* begin_color;
80 const char* end_color;
82 const char* name() { return _name; }
83 const char* outdent() { return indent_string[MAX_INDENT_LEVEL - _indent_level]; }
84 const char* indent() { return indent_string[_indent_level]; }
86 bool is_unknown() { return type == SummaryType::Unknown; }
87 bool is_cpu() { return type == SummaryType::CPU; }
88 bool is_process() { return type == SummaryType::Process; }
89 bool is_thread() { return type == SummaryType::Thread; }
92 template <typename SIZE>
93 void print_summary_line(const Globals& globals, const Machine<SIZE>& machine, AbsInterval summary_interval, struct SummaryLineData& line_data)
96 // Avg Actual Wanted Actual Wanted Jetsam
97 // All CPU Thr Avg Actual Wanted Concurrency Processes To Run Threads To Run VMFault VMFault IO Wait # IO IO Bytes Jetsam Proc
98 // [Time(mS)] Name Run% Idle% Intr% Idle% #Intr #CSW On CPU/µS CPU/mS CPU/mS (# CPU) Ran Processes Ran Threads Count Time (mS) Time (mS) Ops Completed Time (mS) Count
99 // 123456789abcdef0 123456789012345678901234567890 1234567 1234567 1234567 1234567 1234567 12345678 123456789 123456789abc 123456789abc 123456789ab 123456789 123456789 1234567 1234567 1234567 123456789abc 123456789abc 1234567 1234567890 123456789 123456
100 // 1119100000.00 76.58 16.53 6.89 0.00 230 112 10000.00 100000.00 100000.00 1.55 2 3 12 13 2280 230.48 1998.22 3318 123.40 MB 0.00
102 ASSERT(!line_data.is_unknown(), "Sanity");
105 // It turns out that calling dprintf is very expensive; we're going to
106 // accumulate to a string buffer and then flush once at the end.
110 char* line_end = line + sizeof(line);
113 // Begin line coloring (if any)
115 if (line_data.is_colored) {
116 ASSERT(line_data.begin_color && line_data.end_color, "Sanity");
117 cursor += snprintf(cursor, line_end - cursor, "%s", line_data.begin_color);
119 if (cursor > line_end)
123 if (line_data.should_print_timestamp) {
126 // Time and Name get a special indent treatment, so they come out
127 // as heirarchically aligned, while not disturbing the rest of the
128 // columns. The time value is actually outdented, the name value
131 // The result is that you get something like this:
133 // [Time(mS)] Name Run%
134 // 123456789abcdef0 123456789012345678901234567890 1234567
136 // 1000.00 INDENT-LEVEL-0 ##.##
137 // 1000.00 INDENT-LEVEL-1 ##.##
138 // 1000.00 INDENT-LEVEL-2 ##.##
139 // 1000.00 INDENT-LEVEL-3 ##.##
142 char time_buffer[64];
147 if (globals.should_print_mach_absolute_timestamps()) {
148 if (globals.beginning_of_time().value() == 0)
149 snprintf(time_buffer, sizeof(time_buffer), "%llX%s", (summary_interval.location() - globals.beginning_of_time()).value(), line_data.outdent());
151 snprintf(time_buffer, sizeof(time_buffer), "%llu%s", (summary_interval.location() - globals.beginning_of_time()).value(), line_data.outdent());
153 NanoTime ntime = (summary_interval.location() - globals.beginning_of_time()).nano_time(globals.timebase());
154 snprintf(time_buffer, sizeof(time_buffer), "%3.2f%s", (double)ntime.value() / 1000000.0, line_data.outdent());
157 cursor += snprintf(cursor, line_end - cursor, "%16s ", time_buffer);
159 if (cursor > line_end)
168 char name_buffer[64];
169 snprintf(name_buffer, sizeof(name_buffer), "%s%s", line_data.indent(), line_data.name());
171 cursor += snprintf(cursor, line_end - cursor, "%-30s ", name_buffer);
172 if (cursor > line_end)
177 // Run% Idle% Intr% All-CPUs-Idle% #Intr
180 // Special case for process/thread summary lines, print idle/intr as "-";
181 if (line_data.is_process() || line_data.is_thread()) {
182 double run_percent = 0.0;
184 if (line_data.total_time.value() > 0)
185 run_percent = line_data.total_run_time.double_value() / line_data.total_time.double_value() * line_data.percent_multiplier;
187 cursor += snprintf(cursor, line_end - cursor, "%7.2f %7s %7s %7s %7u ",
192 line_data.num_intr_events);
194 ASSERT(line_data.total_time.value() > 0, "Sanity");
196 cursor += snprintf(cursor, line_end - cursor, "%7.2f %7.2f %7.2f %7.2f %7u ",
197 line_data.total_run_time.double_value() / line_data.total_time.double_value() * line_data.percent_multiplier,
198 line_data.total_idle_time.double_value() / line_data.total_time.double_value() * line_data.percent_multiplier,
199 line_data.total_intr_time.double_value() / line_data.total_time.double_value() * line_data.percent_multiplier,
200 line_data.total_all_cpus_idle_time.double_value() / line_data.total_time.double_value() * line_data.percent_multiplier,
201 line_data.num_intr_events);
204 if (cursor > line_end)
208 // #context-switches avg-on-cpu/µS
210 if (line_data.context_switches > 0) {
211 double avg_on_cpu_uS = (line_data.total_run_time / AbsTime(line_data.context_switches)).nano_time(globals.timebase()).value() / 1000.0;
212 cursor += snprintf(cursor, line_end - cursor, "%8u %9.2f ", line_data.context_switches, avg_on_cpu_uS);
214 cursor += snprintf(cursor, line_end - cursor, "%8u %9s ", line_data.context_switches, "-");
217 if (cursor > line_end)
221 // Actual CPU/mS, Wanted CPU/mS
223 if (line_data.total_wanted_run_time > 0) {
224 cursor += snprintf(cursor, line_end - cursor, "%12.2f %12.2f ",
225 (double)line_data.total_run_time.nano_time(globals.timebase()).value() / 1000000.0,
226 (double)(line_data.total_run_time + line_data.total_wanted_run_time).nano_time(globals.timebase()).value() / 1000000.0);
228 cursor += snprintf(cursor, line_end - cursor, "%12.2f %12s ",
229 (double)line_data.total_run_time.nano_time(globals.timebase()).value() / 1000000.0,
233 if (cursor > line_end)
237 // Proc Avg Concurrency
240 if (line_data.total_wallclock_run_time > 0) {
241 cursor += snprintf(cursor, line_end - cursor, "%11.2f ", (double)line_data.total_run_time.value() / (double)line_data.total_wallclock_run_time.value());
242 // cursor += snprintf(cursor, line_end - cursor, "%11.2f ", (double)line_data.total_wallclock_run_time.nano_time(globals.timebase()).value() / 1000000.0);
244 cursor += snprintf(cursor, line_end - cursor, "%11s ", "-");
247 if (cursor > line_end)
251 // Actual Processes, Wanted Processes
253 if (line_data.is_thread()) {
254 cursor += snprintf(cursor, line_end - cursor, "%9s %9s ", "-", "-");
256 if (line_data.total_run_time > 0 && line_data.total_wanted_run_time > 0) {
257 cursor += snprintf(cursor, line_end - cursor, "%9u %9u ", (uint32_t)line_data.actual_process_count, (uint32_t)line_data.wanted_process_count);
258 } else if (line_data.total_run_time > 0) {
259 cursor += snprintf(cursor, line_end - cursor, "%9u %9s ", (uint32_t)line_data.actual_process_count, "-");
260 } else if (line_data.total_wanted_run_time > 0) {
261 cursor += snprintf(cursor, line_end - cursor, "%9s %9u ", "-", (uint32_t)line_data.wanted_process_count);
263 cursor += snprintf(cursor, line_end - cursor, "%9s %9s ", "-", "-");
267 if (cursor > line_end)
271 // Actual Threads, Wanted Threads
273 if (line_data.total_run_time > 0 && line_data.total_wanted_run_time > 0) {
274 cursor += snprintf(cursor, line_end - cursor, "%7u %7u ", (uint32_t)line_data.actual_thread_count, (uint32_t)line_data.wanted_thread_count);
275 } else if (line_data.total_run_time > 0) {
276 cursor += snprintf(cursor, line_end - cursor, "%7u %7s ", (uint32_t)line_data.actual_thread_count, "-");
277 } else if (line_data.total_wanted_run_time > 0) {
278 cursor += snprintf(cursor, line_end - cursor, "%7s %7u ", "-", (uint32_t)line_data.wanted_thread_count);
280 cursor += snprintf(cursor, line_end - cursor, "%7s %7s ", "-", "-");
283 if (cursor > line_end)
288 // #vmfaults, mS blocked in vmfault
290 if (line_data.num_vm_fault_events == 0 && line_data.total_vm_fault_time.value() == 0) {
291 cursor += snprintf(cursor, line_end - cursor, "%7s %12s ", "-", "-");
293 cursor += snprintf(cursor, line_end - cursor, "%7u %12.2f ",
294 line_data.num_vm_fault_events,
295 (double)line_data.total_vm_fault_time.nano_time(globals.timebase()).value() / 1000000.0);
299 // mS blocked on IO activity
301 if (line_data.total_io_time.value() == 0) {
302 cursor += snprintf(cursor, line_end - cursor, "%12s ", "-");
304 cursor += snprintf(cursor, line_end - cursor, "%12.2f ",
305 (double)line_data.total_io_time.nano_time(globals.timebase()).value() / 1000000.0);
311 if (line_data.num_io_events == 0) {
312 cursor += snprintf(cursor, line_end - cursor, "%7s ", "-");
314 cursor += snprintf(cursor, line_end - cursor, "%7u ", line_data.num_io_events);
318 // IO bytes completed
320 if (line_data.io_bytes_completed == 0) {
321 cursor += snprintf(cursor, line_end - cursor, "%10s ", "-");
323 cursor += snprintf(cursor, line_end - cursor, "%10s ", formated_byte_size(line_data.io_bytes_completed).c_str());
329 if (line_data.total_jetsam_time == 0) {
330 cursor += snprintf(cursor, line_end - cursor, "%9s ", "-");
332 cursor += snprintf(cursor, line_end - cursor, "%9.2f ",
333 (double)line_data.total_jetsam_time.nano_time(globals.timebase()).value() / 1000000.0);
339 if (line_data.is_cpu()) {
340 if (line_data.num_jetsam_pids == 0) {
341 cursor += snprintf(cursor, line_end - cursor, "%6s", "-");
343 cursor += snprintf(cursor, line_end - cursor, "%6u", line_data.num_jetsam_pids);
346 cursor += snprintf(cursor, line_end - cursor, "%6s", "");
350 // End line coloring (if any)
352 if (line_data.is_colored) {
353 cursor += snprintf(cursor, line_end - cursor, "%s", line_data.end_color);
355 if (cursor > line_end)
359 dprintf(globals.output_fd(), "%s\n", line);
362 template <typename SIZE>
363 void print_cpu_summary_with_name_and_indent(const Globals& globals, const Machine<SIZE>& machine, AbsInterval summary_interval, const CPUSummary<SIZE>& master_summary, const CPUSummary<SIZE>& cpu_summary, const char* name, uint32_t indent)
365 struct SummaryLineData data(name, indent);
367 data.should_print_timestamp = (globals.is_summary_start_set() || globals.is_summary_stop_set() || globals.is_summary_step_set());
368 data.total_time = master_summary.total_time();
369 data.total_run_time = cpu_summary.total_run_time();
370 data.total_idle_time = cpu_summary.total_idle_time();
371 data.total_intr_time = cpu_summary.total_intr_time();
372 data.total_wanted_run_time = cpu_summary.total_future_run_time();
373 data.total_wallclock_run_time = cpu_summary.total_wallclock_run_time();
374 data.total_all_cpus_idle_time = cpu_summary.total_all_cpus_idle_time();
375 data.total_vm_fault_time = cpu_summary.total_vm_fault_time();
376 data.total_io_time = cpu_summary.total_io_time();
377 data.total_jetsam_time = cpu_summary.total_jetsam_time();
378 data.context_switches = cpu_summary.context_switches();
379 data.num_intr_events = cpu_summary.num_intr_events();
380 data.num_vm_fault_events = cpu_summary.num_vm_fault_events();
381 data.num_io_events = cpu_summary.num_io_events();
382 data.num_jetsam_pids = cpu_summary.num_processes_jetsammed();
383 data.io_bytes_completed = cpu_summary.io_bytes_completed();
384 data.type = SummaryLineData::SummaryType::CPU;
386 for (auto& process_summary : cpu_summary.process_summaries()) {
388 if (process_summary.total_run_time() > 0) {
389 data.actual_process_count++;
390 data.wanted_process_count++;
391 } else if (process_summary.total_future_run_time() > 0) {
392 data.wanted_process_count++;
394 // ASSERT(cpu_summary.total_vm_fault_time() > 0, "Process in summary no actual or wanted run time, and no vm_fault time");
397 for (auto& thread_summary : process_summary.thread_summaries()) {
398 if (thread_summary.total_run_time() > 0) {
399 data.actual_thread_count++;
400 data.wanted_thread_count++;
401 } else if (thread_summary.total_future_run_time() > 0) {
402 data.wanted_thread_count++;
404 // ASSERT((thread_summary.total_vm_fault_time() > 0) || (thread_summary.total_pgin_time() > 0), "Thread in summary no actual or wanted run time, and no vm_fault or pgin time");
409 data.percent_multiplier *= (double)master_summary.active_cpus();
411 print_summary_line(globals, machine, summary_interval, data);
414 template <typename SIZE>
415 void print_process_summary_with_name_and_indent(const Globals& globals, const Machine<SIZE>& machine, AbsInterval summary_interval, const CPUSummary<SIZE>& master_summary, const ProcessSummary<SIZE>& process_summary, const char* name, uint32_t indent)
417 struct SummaryLineData data(name, indent);
419 data.should_print_timestamp = (globals.is_summary_start_set() || globals.is_summary_stop_set() || globals.is_summary_step_set());
420 data.total_run_time = process_summary.total_run_time();
421 data.total_wanted_run_time = process_summary.total_future_run_time();
422 data.total_wallclock_run_time = process_summary.total_wallclock_run_time();
423 data.total_vm_fault_time = process_summary.total_vm_fault_time();
424 data.total_io_time = process_summary.total_io_time();
425 data.total_jetsam_time = process_summary.total_jetsam_time();
426 data.context_switches = process_summary.context_switches();
427 data.num_intr_events = process_summary.num_intr_events();
428 data.actual_process_count = 1;
429 data.wanted_process_count = 1;
430 data.num_vm_fault_events = process_summary.num_vm_fault_events();
431 data.num_io_events = process_summary.num_io_events();
432 data.num_jetsam_pids = process_summary.num_processes_jetsammed();
433 data.io_bytes_completed = process_summary.io_bytes_completed();
434 data.total_time = master_summary.total_time();
435 // This causes the line printer to put "-" in the idle and intr % columns.
436 data.type = SummaryLineData::SummaryType::Process;
437 data.percent_multiplier *= (double)master_summary.active_cpus();
439 // We have to walk the threads to decide actual vs wanted to run
440 for (auto& thread_summary : process_summary.thread_summaries()) {
441 if (thread_summary.total_run_time() > 0) {
442 data.actual_thread_count++;
443 data.wanted_thread_count++;
444 } else if (thread_summary.total_future_run_time() > 0) {
445 data.wanted_thread_count++;
447 // ASSERT(thread_summary.total_vm_fault_time() > 0, "Thread in summary no actual or wanted run time, and no vm_fault time");
451 print_summary_line(globals, machine, summary_interval, data);
454 template <typename SIZE>
455 void print_thread_summary_with_name_and_indent(const Globals& globals, const Machine<SIZE>& machine, AbsInterval summary_interval, const CPUSummary<SIZE>& master_summary, const ThreadSummary<SIZE>& thread_summary, const char* name, uint32_t indent)
457 struct SummaryLineData data(name, indent);
459 /*data.is_colored = true;
460 data.begin_color = TerminalColorStringFor(kTerminalColor::GREEN, true, false);
461 data.end_color = TerminalColorResetString();*/
463 data.should_print_timestamp = (globals.is_summary_start_set() || globals.is_summary_stop_set() || globals.is_summary_step_set());
464 data.total_run_time = thread_summary.total_run_time();
465 data.total_wanted_run_time = thread_summary.total_future_run_time();
466 data.total_vm_fault_time = thread_summary.total_vm_fault_time();
467 data.total_io_time = thread_summary.total_io_time();
468 data.total_jetsam_time = thread_summary.total_jetsam_time();
469 data.context_switches = thread_summary.context_switches();
470 data.num_intr_events = thread_summary.num_intr_events();
471 data.num_vm_fault_events = thread_summary.num_vm_fault_events();
472 data.num_io_events = thread_summary.num_io_events();
473 data.num_jetsam_pids = 0;
474 data.io_bytes_completed = thread_summary.io_bytes_completed();
475 data.total_time = master_summary.total_time();
476 data.percent_multiplier *= (double)master_summary.active_cpus();
477 data.actual_thread_count = 1;
478 data.wanted_thread_count = 1;
480 // This causes the line printer to put "-" in various columns that don't make sense for a thread summary
481 data.type = SummaryLineData::SummaryType::Thread;
483 print_summary_line(globals, machine, summary_interval, data);
486 template <typename SIZE>
487 static void sort_processes(const Globals& globals, const CPUSummary<SIZE>& summary, std::vector<const MachineProcess<SIZE>*>& processes) {
488 switch (globals.sort_key()) {
490 // Sort by Actual CPU, Future CPU, pid
491 std::sort(processes.begin(), processes.end(), [&summary](const MachineProcess<SIZE>* p0, const MachineProcess<SIZE>* p1) -> bool {
492 auto p0_summary = summary.process_summary(p0);
493 auto p1_summary = summary.process_summary(p1);
495 AbsTime p0_run_time = p0_summary->total_run_time();
496 AbsTime p1_run_time = p1_summary->total_run_time();
498 if (p0_run_time == p1_run_time) {
499 AbsTime p0_future_run_time = p0_summary->total_future_run_time();
500 AbsTime p1_future_run_time = p1_summary->total_future_run_time();
502 if (p0_future_run_time == p1_future_run_time)
503 return p0->pid() < p1->pid();
505 return p1_future_run_time < p0_future_run_time;
508 return p1_run_time < p0_run_time;
512 case kSortKey::VMFault:
513 // Sort by VMFault time, #-faults, pid
514 std::sort(processes.begin(), processes.end(), [&summary](const MachineProcess<SIZE>* p0, const MachineProcess<SIZE>* p1) -> bool {
515 auto p0_summary = summary.process_summary(p0);
516 auto p1_summary = summary.process_summary(p1);
518 AbsTime p0_vm_fault_time = p0_summary->total_vm_fault_time();
519 AbsTime p1_vm_fault_time = p1_summary->total_vm_fault_time();
521 if (p0_vm_fault_time == p1_vm_fault_time) {
522 uint32_t p0_vm_fault_count = p0_summary->num_vm_fault_events();
523 uint32_t p1_vm_fault_count = p1_summary->num_vm_fault_events();
525 if (p0_vm_fault_count == p1_vm_fault_count)
526 return p0->pid() < p1->pid();
528 return p1_vm_fault_count < p0_vm_fault_count;
531 return p1_vm_fault_time < p0_vm_fault_time;
535 case kSortKey::IO_Wait:
536 // Sort by IO time, pid
537 std::sort(processes.begin(), processes.end(), [&summary](const MachineProcess<SIZE>* p0, const MachineProcess<SIZE>* p1) -> bool {
538 auto p0_summary = summary.process_summary(p0);
539 auto p1_summary = summary.process_summary(p1);
541 AbsTime p0_io_time = p0_summary->total_io_time();
542 AbsTime p1_io_time = p1_summary->total_io_time();
544 if (p0_io_time == p1_io_time) {
545 uint32_t p0_io_ops = p0_summary->num_io_events();
546 uint32_t p1_io_ops = p1_summary->num_io_events();
548 if (p0_io_ops == p1_io_ops)
549 return p0->pid() < p1->pid();
551 return p1_io_ops < p0_io_ops;
554 return p1_io_time < p0_io_time;
558 case kSortKey::IO_Ops:
559 // Sort by IO time, pid
560 std::sort(processes.begin(), processes.end(), [&summary](const MachineProcess<SIZE>* p0, const MachineProcess<SIZE>* p1) -> bool {
561 auto p0_summary = summary.process_summary(p0);
562 auto p1_summary = summary.process_summary(p1);
564 uint32_t p0_io_ops = p0_summary->num_io_events();
565 uint32_t p1_io_ops = p1_summary->num_io_events();
567 if (p0_io_ops == p1_io_ops) {
568 AbsTime p0_io_time = p0_summary->total_io_time();
569 AbsTime p1_io_time = p1_summary->total_io_time();
571 if (p0_io_time == p1_io_time)
572 return p0->pid() < p1->pid();
574 return p1_io_time < p0_io_time;
577 return p1_io_ops < p0_io_ops;
581 case kSortKey::IO_Size:
582 // Sort by IO time, pid
583 std::sort(processes.begin(), processes.end(), [&summary](const MachineProcess<SIZE>* p0, const MachineProcess<SIZE>* p1) -> bool {
584 auto p0_summary = summary.process_summary(p0);
585 auto p1_summary = summary.process_summary(p1);
587 uint64_t p0_io_bytes_completed = p0_summary->io_bytes_completed();
588 uint64_t p1_io_bytes_completed = p1_summary->io_bytes_completed();
590 if (p0_io_bytes_completed == p1_io_bytes_completed) {
591 AbsTime p0_io_time = p0_summary->total_io_time();
592 AbsTime p1_io_time = p1_summary->total_io_time();
594 if (p0_io_time == p1_io_time)
595 return p0->pid() < p1->pid();
597 return p1_io_time < p0_io_time;
600 return p1_io_bytes_completed < p0_io_bytes_completed;
606 std::sort(processes.begin(), processes.end(), [](const MachineProcess<SIZE>* p0, const MachineProcess<SIZE>* p1) -> bool {
607 return p0->pid() < p1->pid();
613 template <typename SIZE>
614 static void sort_threads(const Globals& globals, const ProcessSummary<SIZE>& summary, std::vector<const MachineThread<SIZE>*>& threads) {
615 switch (globals.sort_key()) {
617 std::sort(threads.begin(), threads.end(), [&summary](const MachineThread<SIZE>* t0, const MachineThread<SIZE>* t1) -> bool {
618 auto t0_summary = summary.thread_summary(t0);
619 auto t1_summary = summary.thread_summary(t1);
621 AbsTime t0_run_time = t0_summary->total_run_time();
622 AbsTime t1_run_time = t1_summary->total_run_time();
624 if (t0_run_time == t1_run_time) {
625 AbsTime t0_future_run_time = t0_summary->total_future_run_time();
626 AbsTime t1_future_run_time = t1_summary->total_future_run_time();
628 if (t0_future_run_time == t1_future_run_time)
629 return t0->tid() < t1->tid();
631 return t1_future_run_time < t0_future_run_time;
634 return t1_run_time < t0_run_time;
638 case kSortKey::VMFault:
639 // Sort by VMFault time, #-faults, pid
640 std::sort(threads.begin(), threads.end(), [&summary](const MachineThread<SIZE>* t0, const MachineThread<SIZE>* t1) -> bool {
641 auto t0_summary = summary.thread_summary(t0);
642 auto t1_summary = summary.thread_summary(t1);
644 AbsTime t0_vm_fault_time = t0_summary->total_vm_fault_time();
645 AbsTime t1_vm_fault_time = t1_summary->total_vm_fault_time();
647 if (t0_vm_fault_time == t1_vm_fault_time) {
648 uint32_t t0_vm_fault_count = t0_summary->num_vm_fault_events();
649 uint32_t t1_vm_fault_count = t1_summary->num_vm_fault_events();
651 if (t0_vm_fault_count == t1_vm_fault_count)
652 return t0->tid() < t1->tid();
654 return t1_vm_fault_count < t0_vm_fault_count;
657 return t1_vm_fault_time < t0_vm_fault_time;
661 case kSortKey::IO_Wait:
662 // Sort by IO time, pid
663 std::sort(threads.begin(), threads.end(), [&summary](const MachineThread<SIZE>* t0, const MachineThread<SIZE>* t1) -> bool {
664 auto t0_summary = summary.thread_summary(t0);
665 auto t1_summary = summary.thread_summary(t1);
667 AbsTime t0_io_time = t0_summary->total_io_time();
668 AbsTime t1_io_time = t1_summary->total_io_time();
670 if (t0_io_time == t1_io_time) {
671 uint32_t t0_io_ops = t0_summary->num_io_events();
672 uint32_t t1_io_ops = t1_summary->num_io_events();
674 if (t0_io_ops == t1_io_ops)
675 return t0->tid() < t1->tid();
677 return t1_io_ops < t0_io_ops;
680 return t1_io_time < t0_io_time;
684 case kSortKey::IO_Ops:
685 // Sort by IO time, pid
686 std::sort(threads.begin(), threads.end(), [&summary](const MachineThread<SIZE>* t0, const MachineThread<SIZE>* t1) -> bool {
687 auto t0_summary = summary.thread_summary(t0);
688 auto t1_summary = summary.thread_summary(t1);
690 uint32_t t0_io_ops = t0_summary->num_io_events();
691 uint32_t t1_io_ops = t1_summary->num_io_events();
693 if (t0_io_ops == t1_io_ops) {
694 AbsTime t0_io_time = t0_summary->total_io_time();
695 AbsTime t1_io_time = t1_summary->total_io_time();
697 if (t0_io_time == t1_io_time)
698 return t0->tid() < t1->tid();
700 return t1_io_time < t0_io_time;
703 return t1_io_ops < t0_io_ops;
707 case kSortKey::IO_Size:
708 // Sort by IO time, pid
709 std::sort(threads.begin(), threads.end(), [&summary](const MachineThread<SIZE>* t0, const MachineThread<SIZE>* t1) -> bool {
710 auto t0_summary = summary.thread_summary(t0);
711 auto t1_summary = summary.thread_summary(t1);
713 uint64_t t0_io_bytes_completed = t0_summary->io_bytes_completed();
714 uint64_t t1_io_bytes_completed = t1_summary->io_bytes_completed();
716 if (t0_io_bytes_completed == t1_io_bytes_completed) {
717 AbsTime t0_io_time = t0_summary->total_io_time();
718 AbsTime t1_io_time = t1_summary->total_io_time();
720 if (t0_io_time == t1_io_time)
721 return t0->tid() < t1->tid();
723 return t1_io_time < t0_io_time;
726 return t1_io_bytes_completed < t0_io_bytes_completed;
731 std::sort(threads.begin(), threads.end(), [](const MachineThread<SIZE>* t0, const MachineThread<SIZE>* t1) -> bool {
732 return t0->tid() < t1->tid();
738 template <typename SIZE>
739 void print_machine_summary(const Globals& globals, const Machine<SIZE>& machine) {
740 AbsInterval machine_timespan = machine.timespan();
742 AbsTime start(globals.summary_start(machine_timespan));
743 AbsTime stop(globals.summary_stop(machine_timespan));
744 AbsTime step(globals.summary_step(machine_timespan));
746 print_summary_header(globals);
748 AbsInterval start_stop_timespan(start, stop - start);
749 AbsInterval clipped_start_stop_timespan(start_stop_timespan.intersection_range(machine_timespan));
751 start = clipped_start_stop_timespan.location();
752 stop = clipped_start_stop_timespan.max();
754 while (start < stop) {
755 AbsInterval base_interval(start, step);
756 AbsInterval summary_interval(base_interval.intersection_range(clipped_start_stop_timespan));
761 CPUSummary<SIZE> summary = machine.summary_for_timespan(summary_interval, NULL);
764 // We want the TOTAL to include the number of ms elapsed, so print a duration
766 char total_buffer[64];
767 if (globals.should_print_mach_absolute_timestamps()) {
768 if (globals.beginning_of_time().value() == 0)
769 snprintf(total_buffer, sizeof(total_buffer), "TOTAL (0x%llXmabs)", summary_interval.length().value());
771 snprintf(total_buffer, sizeof(total_buffer), "TOTAL (%llumabs)", summary_interval.length().value());
773 NanoTime ntime = summary_interval.length().nano_time(globals.timebase());
774 snprintf(total_buffer, sizeof(total_buffer), "TOTAL (%3.2fms)", (double)ntime.value() / 1000000.0);
776 print_cpu_summary_with_name_and_indent(globals, machine, summary_interval, summary, summary, total_buffer, 0);
778 std::vector<CPUSummary<SIZE>> per_cpu_summaries;
781 // TOTAL per cpu summary
783 if (globals.should_print_cpu_summaries()) {
784 // summary.cpus() is unordered, we want to display sorted by cpu_id.
785 std::vector<const MachineCPU<SIZE>*> sorted_cpus;
787 for (auto& cpu : summary.cpus()) {
788 sorted_cpus.emplace_back(cpu);
791 std::sort(sorted_cpus.begin(), sorted_cpus.end(), [](MachineCPU<SIZE> const* cpu0, MachineCPU<SIZE> const* cpu1) -> bool {
792 return cpu0->id() < cpu1->id();
795 for (auto cpu : sorted_cpus) {
796 per_cpu_summaries.push_back(machine.summary_for_timespan(summary_interval, cpu));
799 snprintf(name, sizeof(name), "CPU%d", cpu->id());
800 print_cpu_summary_with_name_and_indent(globals, machine, summary_interval, summary, per_cpu_summaries.back(), name, 1);
805 // PER PROCESS summary
807 if (globals.should_print_process_summaries()) {
809 // We want to sort the list of processes by PID, so they always display in the same order.
811 std::vector<const MachineProcess<SIZE>*> sorted_processes;
812 for (auto& process_summary : summary.process_summaries()) {
813 sorted_processes.emplace_back(process_summary.process());
816 sort_processes(globals, summary, sorted_processes);
818 for (auto process : sorted_processes) {
819 ASSERT(summary.process_summary(process), "Unable to find process summary by pointer lookup");
820 if (const ProcessSummary<SIZE>* process_summary = summary.process_summary(process)) {
822 snprintf(name, sizeof(name), "%s (%d)%s", process->name(), process->pid(), process->is_exit_by_jetsam() ? " *" : "");
823 print_process_summary_with_name_and_indent(globals, machine, summary_interval, summary, *process_summary, name, 1);
825 if (globals.should_print_cpu_summaries()) {
827 // PER PROCESS per cpu summary
829 for (auto& cpu_summary : per_cpu_summaries) {
830 if (const ProcessSummary<SIZE>* per_cpu_process_summary = cpu_summary.process_summary(process)) {
832 snprintf(name, sizeof(name), "CPU%d %s (%d)", (*cpu_summary.cpus().begin())->id(), process->name(), process->pid());
833 print_process_summary_with_name_and_indent(globals, machine, summary_interval, summary, *per_cpu_process_summary, name, 2);
838 if (globals.should_print_thread_summaries()) {
840 // PER PROCESS per thread summary
842 std::vector<const MachineThread<SIZE>*> sorted_threads;
843 for (auto& thread_summary : process_summary->thread_summaries()) {
844 sorted_threads.emplace_back(thread_summary.thread());
847 sort_threads(globals, *process_summary, sorted_threads);
849 for (auto thread : sorted_threads) {
850 ASSERT(process_summary->thread_summary(thread), "Unable to find thread summary by pointer lookup");
851 if (const ThreadSummary<SIZE>* thread_summary = process_summary->thread_summary(thread)) {
853 snprintf(name, sizeof(name), "tid-%llX", (uint64_t)thread->tid());
854 print_thread_summary_with_name_and_indent(globals, machine, summary_interval, summary, *thread_summary, name, 2);
856 if (globals.should_print_cpu_summaries()) {
858 // PER PROCESS per thread per cpu summary
860 for (auto& cpu_summary : per_cpu_summaries) {
861 if (const ProcessSummary<SIZE>* per_cpu_process_summary = cpu_summary.process_summary(process)) {
862 if (const ThreadSummary<SIZE>* per_cpu_thread_summary = per_cpu_process_summary->thread_summary(thread)) {
864 snprintf(name, sizeof(name), "CPU%d tid-%llX", (*cpu_summary.cpus().begin())->id(), (uint64_t)thread->tid());
865 print_thread_summary_with_name_and_indent(globals, machine, summary_interval, summary, *per_cpu_thread_summary, name, 3);
883 template <typename SIZE>
884 void print_machine_csv_summary_header(const Globals& globals,
885 const Machine<SIZE>& machine,
886 std::vector<const MachineCPU<SIZE>*>& all_cpus,
887 std::vector<const MachineProcess<SIZE>*>& all_processes,
888 std::unordered_map<const MachineProcess<SIZE>*, std::vector<const MachineThread<SIZE>*>>& all_threads,
889 const char* header_type)
895 // "", "TOTAL", "CPU0", "CPU1", "proc1", "proc1-tid1", "proc1-tid2", "proc2", etc..
898 // It turns out that calling dprintf is very expensive; we're going to
899 // accumulate to a string buffer and then flush once at the end.
901 char line[16384]; // Header lines can be big!
903 char* line_end = line + sizeof(line);
908 cursor += snprintf(cursor, line_end - cursor, "%s\n\nTIME, TOTAL", header_type);
909 if (cursor > line_end)
913 // TOTAL per cpu summary
915 if (globals.should_print_cpu_summaries()) {
916 for (auto cpu : all_cpus) {
917 cursor += snprintf(cursor, line_end - cursor, ", CPU%d", cpu->id());
918 if (cursor > line_end)
924 // PER PROCESS summary
926 if (globals.should_print_process_summaries()) {
927 for (auto process : all_processes) {
928 cursor += snprintf(cursor, line_end - cursor, ", %s (%d)", process->name(), process->pid());
929 if (cursor > line_end)
932 if (globals.should_print_cpu_summaries()) {
934 // PER PROCESS per cpu summary
936 for (auto cpu : all_cpus) {
937 cursor += snprintf(cursor, line_end - cursor, ", CPU%d %s (%d)", cpu->id(), process->name(), process->pid());
938 if (cursor > line_end)
943 if (globals.should_print_thread_summaries()) {
945 // PER PROCESS per thread summary
947 for (auto thread : all_threads[process]) {
948 cursor += snprintf(cursor, line_end - cursor, ", tid-%llX", (uint64_t)thread->tid());
949 if (cursor > line_end)
953 // PER PROCESS per thread per cpu summary
955 for (auto cpu : all_cpus) {
956 cursor += snprintf(cursor, line_end - cursor, ", CPU%d tid-%llX", cpu->id(), (uint64_t)thread->tid());
957 if (cursor > line_end)
965 dprintf(globals.output_fd(), "%s\n", line);
968 template <typename SIZE>
969 void print_machine_csv_summary_actual_cpu_ms_line(const Globals& globals,
970 const Machine<SIZE>& machine,
971 AbsInterval summary_interval,
972 std::vector<const MachineCPU<SIZE>*>& all_cpus,
973 std::vector<const MachineProcess<SIZE>*>& all_processes,
974 std::unordered_map<const MachineProcess<SIZE>*, std::vector<const MachineThread<SIZE>*>>& all_threads,
975 CPUSummary<SIZE>& master_summary,
976 std::vector<CPUSummary<SIZE>>& per_cpu_summaries)
978 char line[16384]; // Header lines can be big!
980 char* line_end = line + sizeof(line);
986 if (globals.should_print_mach_absolute_timestamps()) {
987 if (globals.beginning_of_time().value() == 0)
988 cursor += snprintf(cursor, line_end - cursor, "%llX", (summary_interval.location() - globals.beginning_of_time()).value());
990 cursor += snprintf(cursor, line_end - cursor, "%llu", (summary_interval.location() - globals.beginning_of_time()).value());
992 NanoTime ntime = (summary_interval.location() - globals.beginning_of_time()).nano_time(globals.timebase());
993 cursor += snprintf(cursor, line_end - cursor, "%3.2f", (double)ntime.value() / 1000000.0);
996 if (cursor > line_end)
1002 cursor += snprintf(cursor, line_end - cursor, ", %3.2f",
1003 (double)master_summary.total_run_time().nano_time(globals.timebase()).value() / 1000000.0);
1005 if (cursor > line_end)
1009 // TOTAL per cpu summary
1011 if (globals.should_print_cpu_summaries()) {
1012 for (auto& cpu_summary : per_cpu_summaries) {
1013 cursor += snprintf(cursor, line_end - cursor, ", %3.2f",
1014 (double)cpu_summary.total_run_time().nano_time(globals.timebase()).value() / 1000000.0);
1016 if (cursor > line_end)
1022 // PER PROCESS summary
1024 if (globals.should_print_process_summaries()) {
1025 for (auto process : all_processes) {
1026 const ProcessSummary<SIZE>* process_summary;
1028 // Not all summaries will have a matching process entry!
1029 if ((process_summary = master_summary.process_summary(process))) {
1030 cursor += snprintf(cursor, line_end - cursor, ", %3.2f",
1031 (double)process_summary->total_run_time().nano_time(globals.timebase()).value() / 1000000.0);
1033 cursor += snprintf(cursor, line_end - cursor, ",");
1036 if (cursor > line_end)
1039 if (globals.should_print_cpu_summaries()) {
1041 // PER PROCESS per cpu summary
1043 for (auto& cpu_summary : per_cpu_summaries) {
1044 if (const auto& process_summary = cpu_summary.process_summary(process)) {
1045 cursor += snprintf(cursor, line_end - cursor, ", %3.2f",
1046 (double)process_summary->total_run_time().nano_time(globals.timebase()).value() / 1000000.0);
1048 cursor += snprintf(cursor, line_end - cursor, ",");
1051 if (cursor > line_end)
1056 if (globals.should_print_thread_summaries()) {
1058 // PER PROCESS per thread summary
1062 // We again have to do a bit more work, sometime a process is missing and we still need to print empty slots for its threads.
1065 for (auto thread : all_threads[process]) {
1066 if (process_summary) {
1067 if (const auto& thread_summary = process_summary->thread_summary(thread)) {
1068 cursor += snprintf(cursor, line_end - cursor, ", %3.2f",
1069 (double)thread_summary->total_run_time().nano_time(globals.timebase()).value() / 1000000.0);
1071 cursor += snprintf(cursor, line_end - cursor, ",");
1073 cursor += snprintf(cursor, line_end - cursor, ",");
1075 if (cursor > line_end)
1079 if (globals.should_print_cpu_summaries()) {
1081 // PER PROCESS per thread per cpu summary
1083 for (auto& cpu_summary : per_cpu_summaries) {
1084 if (const auto& per_cpu_process_summary = cpu_summary.process_summary(process)) {
1085 if (const auto& per_cpu_thread_summary = per_cpu_process_summary->thread_summary(thread)) {
1086 cursor += snprintf(cursor, line_end - cursor, ", %3.2f",
1087 (double)per_cpu_thread_summary->total_run_time().nano_time(globals.timebase()).value() / 1000000.0);
1089 cursor += snprintf(cursor, line_end - cursor, ",");
1091 cursor += snprintf(cursor, line_end - cursor, ",");
1093 if (cursor > line_end)
1102 dprintf(globals.output_fd(), "%s\n", line);
1105 template <typename SIZE>
1106 void print_machine_csv_summary_wanted_cpu_ms_line(const Globals& globals,
1107 const Machine<SIZE>& machine,
1108 AbsInterval summary_interval,
1109 std::vector<const MachineCPU<SIZE>*>& all_cpus,
1110 std::vector<const MachineProcess<SIZE>*>& all_processes,
1111 std::unordered_map<const MachineProcess<SIZE>*, std::vector<const MachineThread<SIZE>*>>& all_threads,
1112 CPUSummary<SIZE>& master_summary,
1113 std::vector<CPUSummary<SIZE>>& per_cpu_summaries)
1115 char line[16384]; // Header lines can be big!
1116 char* cursor = line;
1117 char* line_end = line + sizeof(line);
1123 if (globals.should_print_mach_absolute_timestamps()) {
1124 if (globals.beginning_of_time().value() == 0)
1125 cursor += snprintf(cursor, line_end - cursor, "%llX", (summary_interval.location() - globals.beginning_of_time()).value());
1127 cursor += snprintf(cursor, line_end - cursor, "%llu", (summary_interval.location() - globals.beginning_of_time()).value());
1129 NanoTime ntime = (summary_interval.location() - globals.beginning_of_time()).nano_time(globals.timebase());
1130 cursor += snprintf(cursor, line_end - cursor, "%3.2f", (double)ntime.value() / 1000000.0);
1133 if (cursor > line_end)
1139 cursor += snprintf(cursor, line_end - cursor, ", %3.2f",
1140 (double)master_summary.total_future_run_time().nano_time(globals.timebase()).value() / 1000000.0);
1142 if (cursor > line_end)
1146 // TOTAL per cpu summary
1148 if (globals.should_print_cpu_summaries()) {
1149 for (auto& cpu_summary : per_cpu_summaries) {
1150 cursor += snprintf(cursor, line_end - cursor, ", %3.2f",
1151 (double)cpu_summary.total_future_run_time().nano_time(globals.timebase()).value() / 1000000.0);
1153 if (cursor > line_end)
1159 // PER PROCESS summary
1161 if (globals.should_print_process_summaries()) {
1162 for (auto process : all_processes) {
1163 const ProcessSummary<SIZE>* process_summary;
1165 // Not all summaries will have a matching process entry!
1166 if ((process_summary = master_summary.process_summary(process))) {
1167 cursor += snprintf(cursor, line_end - cursor, ", %3.2f",
1168 (double)process_summary->total_future_run_time().nano_time(globals.timebase()).value() / 1000000.0);
1170 cursor += snprintf(cursor, line_end - cursor, ",");
1173 if (cursor > line_end)
1176 if (globals.should_print_cpu_summaries()) {
1178 // PER PROCESS per cpu summary
1180 for (auto& cpu_summary : per_cpu_summaries) {
1181 if (const auto& process_summary = cpu_summary.process_summary(process)) {
1182 cursor += snprintf(cursor, line_end - cursor, ", %3.2f",
1183 (double)process_summary->total_future_run_time().nano_time(globals.timebase()).value() / 1000000.0);
1185 cursor += snprintf(cursor, line_end - cursor, ",");
1188 if (cursor > line_end)
1193 if (globals.should_print_thread_summaries()) {
1195 // PER PROCESS per thread summary
1199 // We again have to do a bit more work, sometime a process is missing and we still need to print empty slots for its threads.
1202 for (auto thread : all_threads[process]) {
1203 if (process_summary) {
1204 if (const auto& thread_summary = process_summary->thread_summary(thread)) {
1205 cursor += snprintf(cursor, line_end - cursor, ", %3.2f",
1206 (double)thread_summary->total_future_run_time().nano_time(globals.timebase()).value() / 1000000.0);
1208 cursor += snprintf(cursor, line_end - cursor, ",");
1210 cursor += snprintf(cursor, line_end - cursor, ",");
1212 if (cursor > line_end)
1216 if (globals.should_print_cpu_summaries()) {
1218 // PER PROCESS per thread per cpu summary
1220 for (auto& cpu_summary : per_cpu_summaries) {
1221 if (const auto& per_cpu_process_summary = cpu_summary.process_summary(process)) {
1222 if (const auto& per_cpu_thread_summary = per_cpu_process_summary->thread_summary(thread)) {
1223 cursor += snprintf(cursor, line_end - cursor, ", %3.2f",
1224 (double)per_cpu_thread_summary->total_future_run_time().nano_time(globals.timebase()).value() / 1000000.0);
1226 cursor += snprintf(cursor, line_end - cursor, ",");
1228 cursor += snprintf(cursor, line_end - cursor, ",");
1230 if (cursor > line_end)
1239 dprintf(globals.output_fd(), "%s\n", line);
1242 template <typename SIZE>
1243 void print_machine_csv_summary(const Globals& globals, const Machine<SIZE>& machine) {
1244 AbsInterval machine_timespan = machine.timespan();
1246 AbsTime start(globals.summary_start(machine_timespan));
1247 AbsTime stop(globals.summary_stop(machine_timespan));
1248 AbsTime step(globals.summary_step(machine_timespan));
1250 AbsInterval start_stop_timespan(start, stop - start);
1251 AbsInterval clipped_start_stop_timespan(start_stop_timespan.intersection_range(machine_timespan));
1253 start = clipped_start_stop_timespan.location();
1254 stop = clipped_start_stop_timespan.max();
1257 // While printing a csv summary, we need to use the entire set of processes/threads/cpus
1258 // from the range, even though they may not run in each sample. We first gather a summary
1259 // for the entire time, to get the master list.
1261 CPUSummary<SIZE> start_stop_summary = machine.summary_for_timespan(clipped_start_stop_timespan, NULL);
1263 std::vector<const MachineProcess<SIZE>*> all_processes;
1264 std::vector<const MachineCPU<SIZE>*> all_cpus;
1265 std::unordered_map<const MachineProcess<SIZE>*, std::vector<const MachineThread<SIZE>*>> all_threads;
1268 // gather all processes
1271 for (auto& process_summary : start_stop_summary.process_summaries()) {
1272 all_processes.emplace_back(process_summary.process());
1275 sort_processes(globals, start_stop_summary, all_processes);
1281 if (globals.should_print_cpu_summaries()) {
1282 for (auto& cpu : start_stop_summary.cpus()) {
1283 all_cpus.emplace_back(cpu);
1286 std::sort(all_cpus.begin(), all_cpus.end(), [](MachineCPU<SIZE> const* cpu0, MachineCPU<SIZE> const* cpu1) -> bool {
1287 return cpu0->id() < cpu1->id();
1292 // gather all threads
1294 if (globals.should_print_thread_summaries()) {
1295 for (auto process : all_processes) {
1296 ASSERT(start_stop_summary.process_summary(process), "Unable to find process summary by pointer lookup");
1297 if (const ProcessSummary<SIZE>* process_summary = start_stop_summary.process_summary(process)) {
1299 // PER PROCESS per thread summary
1301 auto& sorted_threads = all_threads[process];
1302 for (auto& thread_summary : process_summary->thread_summaries()) {
1303 sorted_threads.emplace_back(thread_summary.thread());
1306 sort_threads(globals, *process_summary, sorted_threads);
1311 print_machine_csv_summary_header(globals, machine, all_cpus, all_processes, all_threads, "Actual CPU/ms");
1313 while (start < stop) {
1314 AbsInterval base_interval(start, step);
1315 AbsInterval summary_interval(base_interval.intersection_range(clipped_start_stop_timespan));
1320 CPUSummary<SIZE> summary = machine.summary_for_timespan(summary_interval, NULL);
1323 // Per CPU summaries...
1325 std::vector<CPUSummary<SIZE>> per_cpu_summaries;
1326 if (globals.should_print_cpu_summaries()) {
1327 for (auto cpu : all_cpus) {
1328 per_cpu_summaries.push_back(machine.summary_for_timespan(summary_interval, cpu));
1332 print_machine_csv_summary_actual_cpu_ms_line(globals, machine, summary_interval, all_cpus, all_processes, all_threads, summary, per_cpu_summaries);
1339 // Now print Wanted CPU/ms
1341 start = clipped_start_stop_timespan.location();
1342 stop = clipped_start_stop_timespan.max();
1344 dprintf(globals.output_fd(), "\n");
1345 print_machine_csv_summary_header(globals, machine, all_cpus, all_processes, all_threads, "Wanted CPU/ms");
1347 while (start < stop) {
1348 AbsInterval base_interval(start, step);
1349 AbsInterval summary_interval(base_interval.intersection_range(clipped_start_stop_timespan));
1354 CPUSummary<SIZE> summary = machine.summary_for_timespan(summary_interval, NULL);
1357 // Per CPU summaries...
1359 std::vector<CPUSummary<SIZE>> per_cpu_summaries;
1360 if (globals.should_print_cpu_summaries()) {
1361 for (auto cpu : all_cpus) {
1362 per_cpu_summaries.push_back(machine.summary_for_timespan(summary_interval, cpu));
1366 print_machine_csv_summary_wanted_cpu_ms_line(globals, machine, summary_interval, all_cpus, all_processes, all_threads, summary, per_cpu_summaries);
1372 template <typename SIZE>
1373 void print_process_start_stop_timestamps(const Globals& globals, const Machine<SIZE>& machine) {
1374 for (auto process : machine.processes()) {
1377 // Skip processes with no events
1380 if (process->timespan().length() == 0) {
1381 // Skip processes with nothing in them.
1382 // The assert may be too strong.
1383 ASSERT(process->is_created_by_thread_map(), "Expected a zero length process to be from the thread map");
1388 // Don't print the kernel process, it will occupy the entire trace
1390 if (process->is_kernel())
1396 char time_buffer[64];
1397 if (globals.beginning_of_time().value() == 0)
1398 snprintf(time_buffer, sizeof(time_buffer), "%llumabs", process->timespan().location().value());
1400 snprintf(time_buffer, sizeof(time_buffer), "%llumabs", (process->timespan().location() - globals.beginning_of_time()).value());
1405 char end_time_buffer[64];
1406 if (globals.beginning_of_time().value() == 0)
1407 snprintf(end_time_buffer, sizeof(end_time_buffer), "%llumabs", process->timespan().max().value());
1409 snprintf(end_time_buffer, sizeof(end_time_buffer), "%llumabs", (process->timespan().max() - globals.beginning_of_time()).value());
1411 const char* create_reason;
1412 if (process->is_created_by_thread_map())
1413 create_reason = "Threadmap Entry";
1414 else if (process->is_created_by_previous_machine_state())
1415 create_reason = "Prev Machine State";
1416 else if (process->is_created_by_fork_exec())
1417 create_reason = "ForkExec";
1418 else if (process->is_created_by_exec())
1419 create_reason = "Exec";
1421 create_reason = "???";
1423 if (globals.is_verbose()) {
1424 printf(" %30s (%6d) --start %-16s --stop %-16s\tCreated by %-18s %s\n",
1430 process->is_trace_terminated() ? "EXITED" : "");
1432 printf(" %30s (%6d) --start %s --stop %s\n",
1441 template <typename SIZE>
1442 void print_verbose_machine_info(const Globals& globals, const Machine<SIZE>& machine, uint32_t threadmap_count, uint32_t cpumap_count) {
1443 dprintf(globals.output_fd(), "\tEvent data is %s, and appears to be from %s\n", SIZE::is_64_bit ? "K64" : "K32", machine.is_ios() ? "iOS" : "OSX");
1444 dprintf(globals.output_fd(), "\tUsing a%stimebase of %d/%d\n", globals.is_timebase_set() ? " [User Set] " : " ", globals.timebase().numer, globals.timebase().denom);
1446 if (threadmap_count) {
1447 dprintf(globals.output_fd(), "\tA threadmap is present, and contains %u entries\n", threadmap_count);
1449 dprintf(globals.output_fd(), "\tA threadmap is not present");
1453 dprintf(globals.output_fd(), "\tA cpumap is present, and contains %u entries\n", cpumap_count);
1456 dprintf(globals.output_fd(), "\tA cpumap is not present, the system provided a default with %u cpus and %u iops\n", globals.cpu_count(), globals.iop_count());
1459 dprintf(globals.output_fd(), "\tFound %u active cpus in trace data\n", machine.active_cpus());
1461 if (globals.is_summary_start_set()) {
1462 AbsInterval machine_timespan = machine.timespan();
1464 if (globals.should_print_mach_absolute_timestamps()) {
1465 if (globals.beginning_of_time().value() == 0)
1466 dprintf(globals.output_fd(), "\tUsing a --start value of 0x%llXmabs (raw)\n", globals.summary_start(machine_timespan).value());
1468 dprintf(globals.output_fd(), "\tUsing a --start value of %llumabs\n", (globals.summary_start(machine_timespan) - machine_timespan.location()).value());
1470 NanoTime ntime = (globals.summary_start(machine_timespan) - machine_timespan.location()).nano_time(globals.timebase());
1471 dprintf(globals.output_fd(), "\tUsing a --start value of %3.2fms\n", (double)ntime.value() / 1000000.0);
1475 if (globals.is_summary_stop_set()) {
1476 AbsInterval machine_timespan = machine.timespan();
1478 if (globals.should_print_mach_absolute_timestamps()) {
1479 if (globals.beginning_of_time().value() == 0)
1480 dprintf(globals.output_fd(), "\tUsing a --stop value of 0x%llXmabs (raw)\n", globals.summary_stop(machine_timespan).value());
1482 dprintf(globals.output_fd(), "\tUsing a --stop value of %llumabs\n", (globals.summary_stop(machine_timespan) - machine_timespan.location()).value());
1484 NanoTime ntime = (globals.summary_stop(machine_timespan) - machine_timespan.location()).nano_time(globals.timebase());
1485 dprintf(globals.output_fd(), "\tUsing a --stop value of %3.2fms\n", (double)ntime.value() / 1000000.0);
1489 if (globals.is_summary_step_set()) {
1490 AbsInterval machine_timespan = machine.timespan();
1492 if (globals.should_print_mach_absolute_timestamps()) {
1493 if (globals.beginning_of_time().value() == 0)
1494 dprintf(globals.output_fd(), "\tUsing a --step value of 0x%llXmabs (raw)\n", globals.summary_step(machine_timespan).value());
1496 dprintf(globals.output_fd(), "\tUsing a --step value of %llumabs\n", globals.summary_step(machine_timespan).value());
1498 NanoTime ntime = globals.summary_step(machine_timespan).nano_time(globals.timebase());
1499 dprintf( globals.output_fd(), "\tUsing a --step value of %3.2fms\n", (double)ntime.value() / 1000000.0);