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

Side by Side Diff: base/profiler/native_stack_sampler_mac.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 fixes 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
(Empty)
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
3 // found in the LICENSE file.
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"
16 #include "base/profiler/native_stack_sampler.h"
17 #include "base/strings/string_number_conversions.h"
18 #include "base/time/time.h"
19
20 namespace base {
21
22 namespace {
23
24 // Copy of x86_64 thread context structure.
25 struct ThreadContext {
26 uint64_t rax;
27 uint64_t rbx;
28 uint64_t rcx;
29 uint64_t rdx;
30 uint64_t rdi;
31 uint64_t rsi;
32 uint64_t rbp;
33 uint64_t rsp;
34 uint64_t r8;
35 uint64_t r9;
36 uint64_t r10;
37 uint64_t r11;
38 uint64_t r12;
39 uint64_t r13;
40 uint64_t r14;
41 uint64_t r15;
42 uint64_t rip;
43 uint64_t rflags;
44 uint64_t cs;
45 uint64_t fs;
46 uint64_t gs;
47 };
48
49 // Struct for stack walking.
50 struct StackFrame {
51 struct StackFrame* prev;
52 uintptr_t return_addr;
53 };
54
55 // Overwrites |dst| with StackFrame referenced in |src|. Returns true if stack
56 // jump was successful. Failure implies address in |src| is corrupt.
57 bool SafeJump(const StackFrame* src, StackFrame* dst) {
58 vm_size_t bytesCopied = 0;
59 return vm_read_overwrite(mach_task_self(), (vm_address_t)src,
60 (vm_size_t)sizeof(StackFrame), (vm_address_t)dst,
61 &bytesCopied) == KERN_SUCCESS;
62 }
63
64 // Functions related to retrieving Mach-O Identifer ----------------------------
65 // These functions were cannibalized from Mach-O Identifier procedures found in
66 // breakpad/src/common/mac. Support for non-X86_64 architectures and MD5 IDs
67 // were removed to simplify the code.
68
69 // Returns offset in bytes where the x86_64 header is located in binary
70 // loaded at |module_addr|. Returns 0 if |module_addr| is not a valid FAT
71 // Mach-O binary or has not been built for x86_64.
72 off_t GetMach64HeaderOffset(const void* module_addr) {
73 fat_header hdr;
74 memcpy(&hdr, module_addr, sizeof(hdr));
75 if (hdr.magic != FAT_MAGIC && hdr.magic != FAT_CIGAM)
76 return 0;
77
78 if (hdr.magic == FAT_CIGAM)
79 swap_fat_header(&hdr, NXHostByteOrder());
80
81 // Search all fat architectures for x86_64.
82 off_t offset = sizeof(hdr);
83 fat_arch arch;
84 for (uint32_t i = 0; i < hdr.nfat_arch; ++i) {
85 memcpy(&arch, (const char*)module_addr + offset, sizeof(arch));
86
87 if (NXHostByteOrder() != NX_BigEndian)
88 swap_fat_arch(&arch, 1, NXHostByteOrder());
89
90 if (arch.cputype == CPU_TYPE_X86_64)
91 return arch.offset;
92 offset += sizeof(arch);
93 }
94 return 0;
95 }
96
97 // Returns true if Mach-O binary at |module_addr| was built specifically for
98 // x86_64 cpu architecture.
99 bool IsX64Header(const void* module_addr) {
100 mach_header_64 hdr;
101 memcpy(&hdr, module_addr, sizeof(hdr));
102 if (hdr.magic != MH_MAGIC_64 && hdr.magic != MH_CIGAM_64)
103 return false;
104 if (hdr.magic == MH_CIGAM_64)
105 swap_mach_header_64(&hdr, NXHostByteOrder());
106 if (hdr.cputype != CPU_TYPE_X86_64)
107 return false;
108 return true;
109 }
110
111 // Fills |id| with UUID of x86_64 Mach-O binary loaded at |module_addr|.
112 // |offset| is the offset in bytes into |module_addr| where the x86_64 header
113 // is located. Returns false if header is malformed or the header does not
114 // specify the UUID load command.
115 bool GetX64UUIDAt(const void* module_addr, unsigned char id[16], off_t offset) {
116 mach_header_64 hdr;
117 if (!memcpy(&hdr, (const char*)module_addr + offset, sizeof(hdr)) ||
118 (hdr.magic != MH_MAGIC_64 && hdr.magic != MH_CIGAM_64))
119 return false;
120
121 bool swap = hdr.magic == MH_CIGAM_64;
122 if (swap)
123 swap_mach_header_64(&hdr, NXHostByteOrder());
124
125 // Look through load commands for UUID command.
126 offset += sizeof(hdr);
127 for (uint32_t i = 0; i < hdr.ncmds; ++i) {
128 load_command cmd;
129 memcpy(&cmd, (const char*)module_addr + offset, sizeof(cmd));
130
131 if (swap)
132 swap_load_command(&cmd, NXHostByteOrder());
133 if (cmd.cmd == LC_UUID) {
134 uuid_command uuid_cmd;
135 memcpy(&uuid_cmd, (const char*)module_addr + offset, sizeof(uuid_cmd));
136 if (swap)
137 swap_uuid_command(&uuid_cmd, NXHostByteOrder());
138 memcpy(id, &uuid_cmd, sizeof(uuid_cmd));
139 return true;
140 }
141 offset += cmd.cmdsize;
142 }
143 return false;
144 }
145
146 // Fills |id| with Mach-O UUID retrieved from Mach-O binary loaded at
147 // |module_addr|. This function returns false if the binary was not built for
148 // X86_64 or if UUID cannot be found.
149 bool GetUUID(const void* module_addr, unsigned char id[16]) {
150 off_t offset = 0;
151 if (!IsX64Header(module_addr) &&
152 !(offset = GetMach64HeaderOffset(module_addr)))
153 return false;
154 return GetX64UUIDAt(module_addr, id, offset);
155 }
156
157 // Returns hex encoding of a 16-byte ID for binary loaded at |module_addr|.
158 // Returns empty string if UUID cannot be found at |module_addr|.
159 std::string GetUniqueId(const void* module_addr) {
160 unsigned char id[16];
161 if (!GetUUID(module_addr, id))
162 return "";
163 return HexEncode(id, 16);
164 }
165
166 // Functions related to grabbing a stack trace --------------------------------
167
168 // Fills |state| with |target_thread|'s context.
169 bool GetThreadState(thread_act_t target_thread, ThreadContext* state) {
170 mach_msg_type_number_t count =
171 static_cast<mach_msg_type_number_t>(MACHINE_THREAD_STATE_COUNT);
172 return thread_get_state(target_thread, x86_THREAD_STATE64,
173 reinterpret_cast<thread_state_t>(state),
174 &count) == KERN_SUCCESS;
175 }
176
177 // Walks |thread_handle|'s stack and fills |instruction_pointers|.
178 // Returns number of frames in stack, unless there's a corrupt ebp, in which
179 // this function will return the number of frames up until the frame with
180 // a corrupt ebp. This procedure occurs while thread is suspended, so it should
181 // take as little time as possible.
182 int RecordStack(mach_port_t thread_handle,
183 int max_stack_size,
184 const void* instruction_pointers[]) {
185 ThreadContext state;
186 if (!GetThreadState(thread_handle, &state))
187 return 0;
188
189 StackFrame frame;
190 frame.prev = reinterpret_cast<StackFrame*>(state.rbp);
191 int i = 0;
192 for (; i < max_stack_size; i++) {
193 if (!SafeJump(frame.prev, &frame) || !frame.return_addr)
194 return i;
195 instruction_pointers[i] = reinterpret_cast<const void*>(frame.return_addr);
196 }
197 return i;
198 }
199
200 // Adds library referenced by |instruction_pointer| to |sample| and |modules|.
201 // Records library's (1) filepath, (2) base address, and (3) unique ID.
202 void AddModule(StackSamplingProfiler::Sample* sample,
203 std::vector<StackSamplingProfiler::Module>* modules,
204 std::map<void*, size_t>* module_to_index,
205 const void* instruction_pointer) {
206 char filepath[PATH_MAX + 1];
207 Dl_info inf;
208 dladdr(instruction_pointer, &inf);
209 auto loc = module_to_index->find(inf.dli_fbase);
210 if (loc == module_to_index->end()) {
211 realpath(inf.dli_fname, filepath);
212 StackSamplingProfiler::Module module(
213 inf.dli_fbase, GetUniqueId(inf.dli_fbase), base::FilePath(filepath));
214 modules->push_back(module);
215
216 loc = module_to_index->insert(
217 std::make_pair(inf.dli_fbase, modules->size() - 1))
218 .first;
219 }
220 sample->push_back(
221 StackSamplingProfiler::Frame(instruction_pointer, loc->second));
222 }
223
224 // Fills |sample| with Frames and Modules referenced by |instruction_pointers|.
225 void FillSample(const void* instruction_pointers[],
226 std::vector<StackSamplingProfiler::Module>* modules,
227 int stack_depth,
228 StackSamplingProfiler::Sample* sample) {
229 std::map<void*, size_t> module_index;
230 for (int i = 0; i < stack_depth; i++) {
231 AddModule(sample, modules, &module_index, instruction_pointers[i]);
232 }
233 }
234
235 // NativeStackSamplerMac ------------------------------------------------------
236
237 class NativeStackSamplerMac : public NativeStackSampler {
238 public:
239 explicit NativeStackSamplerMac(pid_t thread_handle);
240 ~NativeStackSamplerMac() override;
241
242 // StackSamplingProfiler::NativeStackSampler:
243 void ProfileRecordingStarting(
244 std::vector<StackSamplingProfiler::Module>* modules) override;
245 void RecordStackSample(StackSamplingProfiler::Sample* sample) override;
246 void ProfileRecordingStopped() override;
247
248 private:
249 mach_port_t thread_handle_;
250
251 // Weak. Points to the modules associated with the profile being recorded
252 // between ProfileRecordingStarting() and ProfileRecordingStopped().
253 std::vector<StackSamplingProfiler::Module>* current_modules_;
254
255 DISALLOW_COPY_AND_ASSIGN(NativeStackSamplerMac);
256 };
257
258 // The PlatformThreadId of the given thread is actually a typedef of
259 // mach_port_t.
260 // (base/threading/platform_thread_posix.cc:128)
261 NativeStackSamplerMac::NativeStackSamplerMac(pid_t thread_handle)
262 : thread_handle_(static_cast<mach_port_t>(thread_handle)) {}
263
264 NativeStackSamplerMac::~NativeStackSamplerMac() {}
265
266 void NativeStackSamplerMac::ProfileRecordingStarting(
267 std::vector<StackSamplingProfiler::Module>* modules) {
268 current_modules_ = modules;
269 }
270
271 void NativeStackSamplerMac::RecordStackSample(
272 StackSamplingProfiler::Sample* sample) {
273 DCHECK(current_modules_);
274 const int max_stack_size = 64;
275 const void* instruction_pointers[max_stack_size] = {0};
276
277 thread_suspend(thread_handle_);
278 int stack_depth =
279 RecordStack(thread_handle_, max_stack_size, instruction_pointers);
280 thread_resume(thread_handle_);
281 FillSample(instruction_pointers, current_modules_, stack_depth, sample);
282 }
283
284 void NativeStackSamplerMac::ProfileRecordingStopped() {
285 current_modules_ = nullptr;
286 }
287
288 } // namespace
289
290 scoped_ptr<NativeStackSampler> NativeStackSampler::Create(
291 PlatformThreadId thread_id) {
292 #if defined(OS_MACOSX) && !defined(OS_NACL)
293 return scoped_ptr<NativeStackSampler>(new NativeStackSamplerMac(thread_id));
294 #endif
295 return scoped_ptr<NativeStackSampler>();
296 }
297
298 } // 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