Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(159)

Side by Side Diff: base/profiler/native_stack_sampler_posix.cc

Issue 1342453003: Stack sampling profiler to profile on Mac. TEST-- not for commit. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: BUILD.gn update Created 5 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « base/base.gypi ('k') | base/profiler/stack_sampling_profiler_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include <dlfcn.h>
6 #include <mach/mach.h>
7 #include <mach/thread_status.h>
8 #include <mach-o/swap.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11
12 #include <map>
13
14 #include "base/logging.h"
15 #include "base/md5.h"
5 #include "base/profiler/native_stack_sampler.h" 16 #include "base/profiler/native_stack_sampler.h"
17 #include "base/time/time.h"
6 18
7 namespace base { 19 namespace base {
8 20
21 namespace {
22
23 // Copy of x86_64 thread context structure.
24 struct ThreadContext {
25 uint64_t rax;
26 uint64_t rbx;
27 uint64_t rcx;
28 uint64_t rdx;
29 uint64_t rdi;
30 uint64_t rsi;
31 uint64_t rbp;
32 uint64_t rsp;
33 uint64_t r8;
34 uint64_t r9;
35 uint64_t r10;
36 uint64_t r11;
37 uint64_t r12;
38 uint64_t r13;
39 uint64_t r14;
40 uint64_t r15;
41 uint64_t rip;
42 uint64_t rflags;
43 uint64_t cs;
44 uint64_t fs;
45 uint64_t gs;
46 };
47
48 // Struct for stack walking.
49 struct StackFrame {
50 struct StackFrame* prev;
51 uintptr_t return_addr;
52 };
53
54 // Overwrites |dst| with StackFrame referenced in |src|. Returns true if stack
55 // jump was successful. Failure implies address in |src| is corrupt.
56 bool SafeJump(const StackFrame* src, StackFrame* dst) {
57 vm_size_t bytesCopied = 0;
58 return vm_read_overwrite(mach_task_self(), (vm_address_t)src,
59 (vm_size_t)sizeof(StackFrame), (vm_address_t)dst,
60 &bytesCopied) == KERN_SUCCESS;
61 }
62
63 // Functions related to retrieving Mach-O Identifer ----------------------------
64 // These functions were cannibalized from Mach-O Identifier procedures found in
65 // breakpad/src/common/mac. Support for non-X86_64 architectures and MD5 IDs
66 // were removed to simplify the code.
67
68 // Converts |identifier| to a hexstring and writes to |id_dst|.
69 void ConvertIdentifierToString(const unsigned char identifier[16],
Robert Sesek 2015/09/14 18:55:54 Can you use HexEncode() from base/strings/string_n
70 std::string* id_dst) {
71 char buffer[33];
72 int buffer_idx = 0;
73 for (int idx = 0; idx < 16; ++idx) {
74 int hi = (identifier[idx] >> 4) & 0x0F;
75 int lo = (identifier[idx]) & 0x0F;
76
77 buffer[buffer_idx++] =
78 static_cast<char>((hi >= 10) ? ('A' + hi - 10) : ('0' + hi));
79 buffer[buffer_idx++] =
80 static_cast<char>((lo >= 10) ? ('A' + lo - 10) : ('0' + lo));
81 }
82
83 // NULL terminate
84 buffer[32] = 0;
85 *id_dst = std::string(buffer);
86 }
87
88 // Returns offset in bytes where the x86_64 file header is located in |fd|.
89 // Returns 0 if any read fails, or |fd| is not a valid FAT Mach-O file or has
90 // not been built for x86_64.
91 off_t GetMach64HeaderOffset(int fd) {
92 fat_header hdr;
93 if (!pread(fd, &hdr, sizeof(hdr), 0) ||
94 (hdr.magic != FAT_MAGIC && hdr.magic != FAT_CIGAM))
95 return 0;
96
97 if (hdr.magic == FAT_CIGAM)
98 swap_fat_header(&hdr, NXHostByteOrder());
99
100 // Search all fat architectures for x86_64.
101 off_t offset = sizeof(hdr);
102 fat_arch arch;
103 for (uint32_t i = 0; i < hdr.nfat_arch; ++i) {
104 if (!pread(fd, &arch, sizeof(arch), offset))
105 return 0;
106
107 if (NXHostByteOrder() != NX_BigEndian)
108 swap_fat_arch(&arch, 1, NXHostByteOrder());
109
110 if (arch.cputype == CPU_TYPE_X86_64)
111 return arch.offset;
112 offset += sizeof(arch);
113 }
114 return 0;
115 }
116
117 // Returns true if Mach-O file referenced by |fd| is built specifically for
118 // x86_64 architectures. Also returns false if read fails.
119 bool IsX64Header(int fd) {
120 mach_header_64 hdr;
121 if (!pread(fd, &hdr, sizeof(hdr), 0) ||
122 (hdr.magic != MH_MAGIC_64 && hdr.magic != MH_CIGAM_64))
123 return false;
124 if (hdr.magic == MH_CIGAM_64)
125 swap_mach_header_64(&hdr, NXHostByteOrder());
126 if (hdr.cputype != CPU_TYPE_X86_64)
127 return false;
128 return true;
129 }
130
131 // Fills |id| with UUID of x86_64 Mach-O file specified by |fd|. |offset| is
132 // the offset in bytes into |fd|, where the x86_64 header is located. Returns
133 // false if header is malformed, a read fails, or the header does not specify
134 // the UUID load command.
135 bool GetX64UUIDAt(int fd, unsigned char id[16], off_t offset) {
136 mach_header_64 hdr;
137 if (!pread(fd, &hdr, sizeof(hdr), offset) ||
138 (hdr.magic != MH_MAGIC_64 && hdr.magic != MH_CIGAM_64))
139 return false;
140
141 bool swap = hdr.magic == MH_CIGAM_64;
142 if (swap)
143 swap_mach_header_64(&hdr, NXHostByteOrder());
144
145 // Look through load commands for UUID command.
146 offset += sizeof(hdr);
147 for (uint32_t i = 0; i < hdr.ncmds; ++i) {
148 load_command cmd;
149 if (!pread(fd, &cmd, sizeof(cmd), offset))
150 return false;
151
152 if (swap)
153 swap_load_command(&cmd, NXHostByteOrder());
154 if (cmd.cmd == LC_UUID) {
155 uuid_command uuid_cmd;
156 if (!pread(fd, &uuid_cmd, sizeof(uuid_cmd), offset))
157 return false;
158 if (swap)
159 swap_uuid_command(&uuid_cmd, NXHostByteOrder());
160 memcpy(id, &uuid_cmd, sizeof(uuid_cmd));
161 return true;
162 }
163 offset += cmd.cmdsize;
164 }
165 return false;
166 }
167
168 // Fills |id| with Mach-O UUID retrieved from Mach-O file specified by |fd|.
169 // This function returns false if |fd| has not been built for X86_64 or if UUID
170 // cannot be found.
171 bool GetUUID(int fd, unsigned char id[16]) {
172 off_t offset = 0;
173 if (!IsX64Header(fd) && !(offset = GetMach64HeaderOffset(fd)))
174 return false;
175 return GetX64UUIDAt(fd, id, offset);
176 }
177
178 // Obtains an ID of binary specified by |path| and loads it into |id_dst|.
179 // Returns false and sets |id_dst| to empty string if GetUUID fails.
180 bool GetUniqueId(const char* path, std::string* id_dst) {
181 unsigned char id[16];
182 int fd = open(path, O_RDONLY);
183 if (!GetUUID(fd, id)) {
184 close(fd);
185 *id_dst = "";
186 return false;
187 }
188 close(fd);
189 ConvertIdentifierToString(id, id_dst);
190 return true;
191 }
192
193 // Functions related to grabbing a stack trace --------------------------------
194
195 // Fills |state| with |target_thread|'s context.
196 bool GetThreadState(thread_act_t target_thread, ThreadContext* state) {
197 mach_msg_type_number_t count =
198 static_cast<mach_msg_type_number_t>(MACHINE_THREAD_STATE_COUNT);
199 return thread_get_state(target_thread, x86_THREAD_STATE64,
200 reinterpret_cast<thread_state_t>(state),
201 &count) == KERN_SUCCESS;
202 }
203
204 // Walks |thread_handle|'s stack and fills |instruction_pointers|.
205 // Returns number of frames in stack, unless there's a corrupt ebp, in which
206 // this function will return the number of frames up until the frame with
207 // a corrupt ebp. This procedure occurs while thread is suspended, so it should
208 // take as little time as possible.
209 int RecordStack(mach_port_t thread_handle,
210 int max_stack_size,
211 const void* instruction_pointers[]) {
212 ThreadContext state;
213 if (!GetThreadState(thread_handle, &state))
214 return 0;
215
216 StackFrame frame;
217 frame.prev = reinterpret_cast<StackFrame*>(state.rbp);
218 int i = 0;
219 for (; i < max_stack_size; i++) {
220 if (!SafeJump(frame.prev, &frame) || !frame.return_addr)
221 return i;
222 instruction_pointers[i] = reinterpret_cast<const void*>(frame.return_addr);
223 }
224 return i;
225 }
226
227 // Adds library referenced by |instruction_pointer| to |sample| and |modules|.
228 // Records library's (1) filepath, (2) base address, and (3) unique ID.
229 void AddModule(StackSamplingProfiler::Sample* sample,
230 std::vector<StackSamplingProfiler::Module>* modules,
231 std::map<void*, size_t>* module_to_index,
232 const void* instruction_pointer) {
233 char filepath[PATH_MAX + 1];
234 Dl_info inf;
235 dladdr(instruction_pointer, &inf);
236 auto loc = module_to_index->find(inf.dli_fbase);
237 if (loc == module_to_index->end()) {
238 realpath(inf.dli_fname, filepath);
239 std::string unique_id;
240 GetUniqueId(inf.dli_fname, &unique_id);
Robert Sesek 2015/09/14 18:55:54 Touching disk here is going to be a huge hit on so
241 StackSamplingProfiler::Module module(inf.dli_fbase, unique_id,
242 base::FilePath(filepath));
243 modules->push_back(module);
244
245 loc = module_to_index->insert(
246 std::make_pair(inf.dli_fbase, modules->size() - 1))
247 .first;
248 }
249 sample->push_back(
250 StackSamplingProfiler::Frame(instruction_pointer, loc->second));
251 }
252
253 // Fills |sample| with Frames and Modules referenced by |instruction_pointers|.
254 void FillSample(const void* instruction_pointers[],
255 std::vector<StackSamplingProfiler::Module>* modules,
256 int stack_depth,
257 StackSamplingProfiler::Sample* sample) {
258 std::map<void*, size_t> module_index;
259 for (int i = 0; i < stack_depth; i++) {
260 AddModule(sample, modules, &module_index, instruction_pointers[i]);
261 }
262 }
263
264 // NativeStackSamplerMac ------------------------------------------------------
265
266 class NativeStackSamplerMac : public NativeStackSampler {
Robert Sesek 2015/09/14 18:55:54 This should move into native_stack_sampler_mac.cc
267 public:
268 explicit NativeStackSamplerMac(pid_t thread_handle);
269 ~NativeStackSamplerMac() override;
270
271 // StackSamplingProfiler::NativeStackSampler:
272 void ProfileRecordingStarting(
273 std::vector<StackSamplingProfiler::Module>* modules) override;
274 void RecordStackSample(StackSamplingProfiler::Sample* sample) override;
275 void ProfileRecordingStopped() override;
276
277 private:
278 mach_port_t thread_handle_;
279
280 // Weak. Points to the modules associated with the profile being recorded
281 // between ProfileRecordingStarting() and ProfileRecordingStopped().
282 std::vector<StackSamplingProfiler::Module>* current_modules_;
283
284 DISALLOW_COPY_AND_ASSIGN(NativeStackSamplerMac);
285 };
286
287 // The PlatformThreadId of the given thread is actually type mach_port_t.
288 // (See base/threading/platform_thread_posix.cc:128)
Robert Sesek 2015/09/14 18:55:54 This seems like a bug. May want to file one for th
289 NativeStackSamplerMac::NativeStackSamplerMac(pid_t thread_handle)
290 : thread_handle_(static_cast<mach_port_t>(thread_handle)) {}
291
292 NativeStackSamplerMac::~NativeStackSamplerMac() {}
293
294 void NativeStackSamplerMac::ProfileRecordingStarting(
295 std::vector<StackSamplingProfiler::Module>* modules) {
296 current_modules_ = modules;
297 }
298
299 void NativeStackSamplerMac::RecordStackSample(
300 StackSamplingProfiler::Sample* sample) {
301 DCHECK(current_modules_);
302 const int max_stack_size = 64;
303 const void* instruction_pointers[max_stack_size] = {0};
304
305 thread_suspend(thread_handle_);
306 int stack_depth =
307 RecordStack(thread_handle_, max_stack_size, instruction_pointers);
308 thread_resume(thread_handle_);
309 FillSample(instruction_pointers, current_modules_, stack_depth, sample);
310 }
311
312 void NativeStackSamplerMac::ProfileRecordingStopped() {
313 current_modules_ = nullptr;
314 }
315
316 } // namespace
317
9 scoped_ptr<NativeStackSampler> NativeStackSampler::Create( 318 scoped_ptr<NativeStackSampler> NativeStackSampler::Create(
10 PlatformThreadId thread_id) { 319 PlatformThreadId thread_id) {
320 #if defined(OS_MACOSX)
321 return scoped_ptr<NativeStackSampler>(new NativeStackSamplerMac(thread_id));
322 #endif
11 return scoped_ptr<NativeStackSampler>(); 323 return scoped_ptr<NativeStackSampler>();
12 } 324 }
13 325
14 } // namespace base 326 } // namespace base
OLDNEW
« no previous file with comments | « base/base.gypi ('k') | base/profiler/stack_sampling_profiler_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698