]> git.saurik.com Git - apple/system_cmds.git/blob - kdprof/SaveTraceAction.cpp
ce95b375a98d5b1a8e135c73007bde4912efddd8
[apple/system_cmds.git] / kdprof / SaveTraceAction.cpp
1 //
2 // SaveTraceAction.cpp
3 // kdprof
4 //
5 // Created by James McIlree on 5/2/13.
6 // Copyright (c) 2013 Apple. All rights reserved.
7 //
8
9 #include "global.h"
10
11 template <typename SIZE>
12 static void execute_arch_specific(Globals& globals, KDState& state, FileDescriptor& save_fd) {
13 // Collect all data first, printing takes time...
14 auto threadmap = KDBG::threadmap<SIZE>(state);
15 auto cpumap = KDBG::cpumap();
16
17 // These are future proofing, trace doesn't actually need page alignment
18 // here, just file block size alignment. When page sizes go to 16k, we
19 // don't want 16k of padding.
20
21 #define FILE_BLOCK_SIZE 4096
22 #define FILE_BLOCK_SIZE_MASK 4095
23
24 /*
25 * To write a RAW_VERSION1+ file, we must embed a cpumap in the "padding"
26 * used to file block align the events folloing the threadmap. If the
27 * threadmap happens to not require enough padding, we artificially
28 * increase its footprint until it needs enough padding.
29 */
30
31 uint32_t pad_size = FILE_BLOCK_SIZE - ((sizeof(TraceDataHeader<SIZE>) + (threadmap.size() * sizeof(KDThreadMapEntry<SIZE>))) & FILE_BLOCK_SIZE_MASK);
32 uint32_t cpumap_size = sizeof(kd_cpumap_header) + (uint32_t)cpumap.size() * sizeof(KDCPUMapEntry);
33 uint32_t extra_thread_count = 0;
34
35 if (cpumap_size > pad_size) {
36 /* Force an overflow onto the next page, we get a full page of padding */
37 extra_thread_count = (pad_size / sizeof(KDCPUMapEntry)) + 1;
38 }
39
40 // Write the header
41 TraceDataHeader<SIZE> header(RAW_VERSION1, (uint32_t)threadmap.size(), time(NULL), 0);
42 write(save_fd, &header, sizeof(TraceDataHeader<SIZE>));
43
44 // Write the threadmaps
45 write(save_fd, threadmap.data(), threadmap.size() * sizeof(KDThreadMapEntry<SIZE>));
46
47 if (extra_thread_count) {
48 pad_size = extra_thread_count * sizeof(KDThreadMapEntry<SIZE>);
49 auto pad_buf = (uint8_t *)calloc(pad_size, 1);
50 write(save_fd, pad_buf, pad_size);
51 free(pad_buf);
52 }
53
54 // Write the cpumaps & any remaining padding
55 size_t bytes_written = sizeof(TraceDataHeader<SIZE>) + (threadmap.size() + extra_thread_count) * sizeof(KDThreadMapEntry<SIZE>);
56 pad_size = FILE_BLOCK_SIZE - (bytes_written & FILE_BLOCK_SIZE_MASK);
57
58 ASSERT(pad_size >= cpumap.size() * sizeof(KDCPUMapEntry), "Not enough padding bytes!");
59 if (pad_size) {
60 auto cpumap_header = (kd_cpumap_header*)calloc(pad_size, 1);
61 cpumap_header->version_no = RAW_VERSION1;
62 cpumap_header->cpu_count = (uint32_t)cpumap.size();
63 auto cpus = (kd_cpumap*)&cpumap_header[1];
64 memcpy(cpus, cpumap.data(), cpumap.size() * sizeof(KDCPUMapEntry));
65 write(save_fd, cpumap_header, pad_size);
66 }
67
68 // Write the events
69 //
70 // Because this may be used to capture boot traces which consume very
71 // large amounts of memory, we will likely not be able to collect
72 // the entire buffer space in a single shot. Read it in small chunks.
73 //
74 auto twenty_mb = 20 * 1024 * 1024;
75 auto num_events_in_twenty_mb = twenty_mb / sizeof(KDEvent<SIZE>);
76 MemoryBuffer<KDEvent<SIZE>> events(num_events_in_twenty_mb);
77
78 // We read until we don't get back a full buffer, hoping thats enough.
79 while (1) {
80 int count = KDBG::read(events.data(), events.capacity_in_bytes());
81
82 if (count != -1)
83 write(save_fd, events.data(), count * sizeof(KDEvent<SIZE>));
84
85 if (count < num_events_in_twenty_mb) {
86 break;
87 }
88 }
89
90 // close up
91 save_fd.close();
92 }
93
94 void SaveTraceAction::execute(Globals& globals) {
95 KDState state = KDBG::state();
96 if (state.is_lp64()) {
97 execute_arch_specific<Kernel64>(globals, state, _save_fd);
98 } else {
99 execute_arch_specific<Kernel32>(globals, state, _save_fd);
100 }
101 }