PageRenderTime 57ms CodeModel.GetById 28ms RepoModel.GetById 1ms app.codeStats 0ms

/base/profiler/native_stack_sampler_win.cc

https://gitlab.com/jonnialva90/iridium-browser
C++ | 332 lines | 216 code | 50 blank | 66 comment | 24 complexity | b6228d5deaae5c768e414c7e092d525a MD5 | raw file
  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. #include <objbase.h>
  5. #include <windows.h>
  6. #include <map>
  7. #include <utility>
  8. #include "base/logging.h"
  9. #include "base/profiler/native_stack_sampler.h"
  10. #include "base/profiler/win32_stack_frame_unwinder.h"
  11. #include "base/strings/string_util.h"
  12. #include "base/strings/stringprintf.h"
  13. #include "base/strings/utf_string_conversions.h"
  14. #include "base/time/time.h"
  15. #include "base/win/pe_image.h"
  16. #include "base/win/scoped_handle.h"
  17. namespace base {
  18. // Stack recording functions --------------------------------------------------
  19. namespace {
  20. // Walks the stack represented by |context| from the current frame downwards,
  21. // recording the instruction pointers for each frame in |instruction_pointers|.
  22. int RecordStack(CONTEXT* context,
  23. int max_stack_size,
  24. const void* instruction_pointers[],
  25. Win32StackFrameUnwinder* frame_unwinder) {
  26. #ifdef _WIN64
  27. int i = 0;
  28. for (; (i < max_stack_size) && context->Rip; ++i) {
  29. instruction_pointers[i] = reinterpret_cast<const void*>(context->Rip);
  30. if (!frame_unwinder->TryUnwind(context))
  31. return i + 1;
  32. }
  33. return i;
  34. #else
  35. return 0;
  36. #endif
  37. }
  38. // Fills in |module_handles| corresponding to the pointers to code in
  39. // |addresses|. The module handles are returned with reference counts
  40. // incremented and should be freed with FreeModuleHandles. See note in
  41. // SuspendThreadAndRecordStack for why |addresses| and |module_handles| are
  42. // arrays.
  43. void FindModuleHandlesForAddresses(const void* const addresses[],
  44. HMODULE module_handles[], int stack_depth) {
  45. for (int i = 0; i < stack_depth; ++i) {
  46. HMODULE module_handle = NULL;
  47. if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
  48. reinterpret_cast<LPCTSTR>(addresses[i]),
  49. &module_handle)) {
  50. // HMODULE actually represents the base address of the module, so we can
  51. // use it directly as an address.
  52. DCHECK_LE(reinterpret_cast<const void*>(module_handle), addresses[i]);
  53. module_handles[i] = module_handle;
  54. }
  55. }
  56. }
  57. // Frees the modules handles returned by FindModuleHandlesForAddresses. See note
  58. // in SuspendThreadAndRecordStack for why |module_handles| is an array.
  59. void FreeModuleHandles(int stack_depth, HMODULE module_handles[]) {
  60. for (int i = 0; i < stack_depth; ++i) {
  61. if (module_handles[i])
  62. ::FreeLibrary(module_handles[i]);
  63. }
  64. }
  65. // Gets the unique build ID for a module. Windows build IDs are created by a
  66. // concatenation of a GUID and AGE fields found in the headers of a module. The
  67. // GUID is stored in the first 16 bytes and the AGE is stored in the last 4
  68. // bytes. Returns the empty string if the function fails to get the build ID.
  69. //
  70. // Example:
  71. // dumpbin chrome.exe /headers | find "Format:"
  72. // ... Format: RSDS, {16B2A428-1DED-442E-9A36-FCE8CBD29726}, 10, ...
  73. //
  74. // The resulting buildID string of this instance of chrome.exe is
  75. // "16B2A4281DED442E9A36FCE8CBD2972610".
  76. //
  77. // Note that the AGE field is encoded in decimal, not hex.
  78. std::string GetBuildIDForModule(HMODULE module_handle) {
  79. GUID guid;
  80. DWORD age;
  81. win::PEImage(module_handle).GetDebugId(&guid, &age);
  82. const int kGUIDSize = 39;
  83. std::wstring build_id;
  84. int result =
  85. ::StringFromGUID2(guid, WriteInto(&build_id, kGUIDSize), kGUIDSize);
  86. if (result != kGUIDSize)
  87. return std::string();
  88. RemoveChars(build_id, L"{}-", &build_id);
  89. build_id += StringPrintf(L"%d", age);
  90. return WideToUTF8(build_id);
  91. }
  92. // ScopedDisablePriorityBoost -------------------------------------------------
  93. // Disables priority boost on a thread for the lifetime of the object.
  94. class ScopedDisablePriorityBoost {
  95. public:
  96. ScopedDisablePriorityBoost(HANDLE thread_handle);
  97. ~ScopedDisablePriorityBoost();
  98. private:
  99. HANDLE thread_handle_;
  100. BOOL got_previous_boost_state_;
  101. BOOL boost_state_was_disabled_;
  102. DISALLOW_COPY_AND_ASSIGN(ScopedDisablePriorityBoost);
  103. };
  104. ScopedDisablePriorityBoost::ScopedDisablePriorityBoost(HANDLE thread_handle)
  105. : thread_handle_(thread_handle),
  106. got_previous_boost_state_(false),
  107. boost_state_was_disabled_(false) {
  108. got_previous_boost_state_ =
  109. ::GetThreadPriorityBoost(thread_handle_, &boost_state_was_disabled_);
  110. if (got_previous_boost_state_) {
  111. // Confusingly, TRUE disables priority boost.
  112. ::SetThreadPriorityBoost(thread_handle_, TRUE);
  113. }
  114. }
  115. ScopedDisablePriorityBoost::~ScopedDisablePriorityBoost() {
  116. if (got_previous_boost_state_)
  117. ::SetThreadPriorityBoost(thread_handle_, boost_state_was_disabled_);
  118. }
  119. // Suspends the thread with |thread_handle|, records the stack into
  120. // |instruction_pointers|, then resumes the thread. Returns the size of the
  121. // stack.
  122. //
  123. // IMPORTANT NOTE: No heap allocations may occur between SuspendThread and
  124. // ResumeThread. Otherwise this code can deadlock on heap locks acquired by the
  125. // target thread before it was suspended. This is why we pass instruction
  126. // pointers and module handles as preallocated arrays rather than vectors, since
  127. // vectors make it too easy to subtly allocate memory.
  128. int SuspendThreadAndRecordStack(HANDLE thread_handle, int max_stack_size,
  129. const void* instruction_pointers[]) {
  130. Win32StackFrameUnwinder frame_unwinder;
  131. if (::SuspendThread(thread_handle) == -1)
  132. return 0;
  133. int stack_depth = 0;
  134. CONTEXT thread_context = {0};
  135. thread_context.ContextFlags = CONTEXT_FULL;
  136. if (::GetThreadContext(thread_handle, &thread_context)) {
  137. stack_depth = RecordStack(&thread_context, max_stack_size,
  138. instruction_pointers, &frame_unwinder);
  139. }
  140. // Disable the priority boost that the thread would otherwise receive on
  141. // resume. We do this to avoid artificially altering the dynamics of the
  142. // executing application any more than we already are by suspending and
  143. // resuming the thread.
  144. //
  145. // Note that this can racily disable a priority boost that otherwise would
  146. // have been given to the thread, if the thread is waiting on other wait
  147. // conditions at the time of SuspendThread and those conditions are satisfied
  148. // before priority boost is reenabled. The measured length of this window is
  149. // ~100us, so this should occur fairly rarely.
  150. ScopedDisablePriorityBoost disable_priority_boost(thread_handle);
  151. bool resume_thread_succeeded = ::ResumeThread(thread_handle) != -1;
  152. CHECK(resume_thread_succeeded) << "ResumeThread failed: " << GetLastError();
  153. return stack_depth;
  154. }
  155. // NativeStackSamplerWin ------------------------------------------------------
  156. class NativeStackSamplerWin : public NativeStackSampler {
  157. public:
  158. explicit NativeStackSamplerWin(win::ScopedHandle thread_handle);
  159. ~NativeStackSamplerWin() override;
  160. // StackSamplingProfiler::NativeStackSampler:
  161. void ProfileRecordingStarting(
  162. std::vector<StackSamplingProfiler::Module>* modules) override;
  163. void RecordStackSample(StackSamplingProfiler::Sample* sample) override;
  164. void ProfileRecordingStopped() override;
  165. private:
  166. // Attempts to query the module filename, base address, and id for
  167. // |module_handle|, and store them in |module|. Returns true if it succeeded.
  168. static bool GetModuleForHandle(HMODULE module_handle,
  169. StackSamplingProfiler::Module* module);
  170. // Gets the index for the Module corresponding to |module_handle| in
  171. // |modules|, adding it if it's not already present. Returns
  172. // StackSamplingProfiler::Frame::kUnknownModuleIndex if no Module can be
  173. // determined for |module|.
  174. size_t GetModuleIndex(HMODULE module_handle,
  175. std::vector<StackSamplingProfiler::Module>* modules);
  176. // Copies the stack information represented by |instruction_pointers| into
  177. // |sample| and |modules|.
  178. void CopyToSample(const void* const instruction_pointers[],
  179. const HMODULE module_handles[],
  180. int stack_depth,
  181. StackSamplingProfiler::Sample* sample,
  182. std::vector<StackSamplingProfiler::Module>* modules);
  183. win::ScopedHandle thread_handle_;
  184. // Weak. Points to the modules associated with the profile being recorded
  185. // between ProfileRecordingStarting() and ProfileRecordingStopped().
  186. std::vector<StackSamplingProfiler::Module>* current_modules_;
  187. // Maps a module handle to the corresponding Module's index within
  188. // current_modules_.
  189. std::map<HMODULE, size_t> profile_module_index_;
  190. DISALLOW_COPY_AND_ASSIGN(NativeStackSamplerWin);
  191. };
  192. NativeStackSamplerWin::NativeStackSamplerWin(win::ScopedHandle thread_handle)
  193. : thread_handle_(thread_handle.Take()) {
  194. }
  195. NativeStackSamplerWin::~NativeStackSamplerWin() {
  196. }
  197. void NativeStackSamplerWin::ProfileRecordingStarting(
  198. std::vector<StackSamplingProfiler::Module>* modules) {
  199. current_modules_ = modules;
  200. profile_module_index_.clear();
  201. }
  202. void NativeStackSamplerWin::RecordStackSample(
  203. StackSamplingProfiler::Sample* sample) {
  204. DCHECK(current_modules_);
  205. const int max_stack_size = 64;
  206. const void* instruction_pointers[max_stack_size] = {0};
  207. HMODULE module_handles[max_stack_size] = {0};
  208. int stack_depth = SuspendThreadAndRecordStack(thread_handle_.Get(),
  209. max_stack_size,
  210. instruction_pointers);
  211. FindModuleHandlesForAddresses(instruction_pointers, module_handles,
  212. stack_depth);
  213. CopyToSample(instruction_pointers, module_handles, stack_depth, sample,
  214. current_modules_);
  215. FreeModuleHandles(stack_depth, module_handles);
  216. }
  217. void NativeStackSamplerWin::ProfileRecordingStopped() {
  218. current_modules_ = nullptr;
  219. }
  220. // static
  221. bool NativeStackSamplerWin::GetModuleForHandle(
  222. HMODULE module_handle,
  223. StackSamplingProfiler::Module* module) {
  224. wchar_t module_name[MAX_PATH];
  225. DWORD result_length =
  226. GetModuleFileName(module_handle, module_name, arraysize(module_name));
  227. if (result_length == 0)
  228. return false;
  229. module->filename = base::FilePath(module_name);
  230. module->base_address = reinterpret_cast<uintptr_t>(module_handle);
  231. module->id = GetBuildIDForModule(module_handle);
  232. if (module->id.empty())
  233. return false;
  234. return true;
  235. }
  236. size_t NativeStackSamplerWin::GetModuleIndex(
  237. HMODULE module_handle,
  238. std::vector<StackSamplingProfiler::Module>* modules) {
  239. if (!module_handle)
  240. return StackSamplingProfiler::Frame::kUnknownModuleIndex;
  241. auto loc = profile_module_index_.find(module_handle);
  242. if (loc == profile_module_index_.end()) {
  243. StackSamplingProfiler::Module module;
  244. if (!GetModuleForHandle(module_handle, &module))
  245. return StackSamplingProfiler::Frame::kUnknownModuleIndex;
  246. modules->push_back(module);
  247. loc = profile_module_index_.insert(std::make_pair(
  248. module_handle, modules->size() - 1)).first;
  249. }
  250. return loc->second;
  251. }
  252. void NativeStackSamplerWin::CopyToSample(
  253. const void* const instruction_pointers[],
  254. const HMODULE module_handles[],
  255. int stack_depth,
  256. StackSamplingProfiler::Sample* sample,
  257. std::vector<StackSamplingProfiler::Module>* module) {
  258. sample->clear();
  259. sample->reserve(stack_depth);
  260. for (int i = 0; i < stack_depth; ++i) {
  261. sample->push_back(StackSamplingProfiler::Frame(
  262. reinterpret_cast<uintptr_t>(instruction_pointers[i]),
  263. GetModuleIndex(module_handles[i], module)));
  264. }
  265. }
  266. } // namespace
  267. scoped_ptr<NativeStackSampler> NativeStackSampler::Create(
  268. PlatformThreadId thread_id) {
  269. #if _WIN64
  270. // Get the thread's handle.
  271. HANDLE thread_handle = ::OpenThread(
  272. THREAD_GET_CONTEXT | THREAD_SUSPEND_RESUME | THREAD_QUERY_INFORMATION,
  273. FALSE,
  274. thread_id);
  275. if (thread_handle) {
  276. return scoped_ptr<NativeStackSampler>(new NativeStackSamplerWin(
  277. win::ScopedHandle(thread_handle)));
  278. }
  279. #endif
  280. return scoped_ptr<NativeStackSampler>();
  281. }
  282. } // namespace base