| Index: base/profiler/stack_sampling_profiler_win.cc
 | 
| diff --git a/base/profiler/stack_sampling_profiler_win.cc b/base/profiler/stack_sampling_profiler_win.cc
 | 
| index ba46cf076dbd01a26dcaf2d65df3e9c5830dd2dd..1ccd13412a64496c17fd9d1476e92abb0fff71c0 100644
 | 
| --- a/base/profiler/stack_sampling_profiler_win.cc
 | 
| +++ b/base/profiler/stack_sampling_profiler_win.cc
 | 
| @@ -2,14 +2,17 @@
 | 
|  // Use of this source code is governed by a BSD-style license that can be
 | 
|  // found in the LICENSE file.
 | 
|  
 | 
| -#include "base/profiler/stack_sampling_profiler.h"
 | 
| +#include <objbase.h>
 | 
| +#include <windows.h>
 | 
|  
 | 
| -#include <dbghelp.h>
 | 
|  #include <map>
 | 
|  #include <utility>
 | 
| -#include <windows.h>
 | 
|  
 | 
|  #include "base/logging.h"
 | 
| +#include "base/profiler/native_stack_sampler.h"
 | 
| +#include "base/strings/string_util.h"
 | 
| +#include "base/strings/stringprintf.h"
 | 
| +#include "base/strings/utf_string_conversions.h"
 | 
|  #include "base/time/time.h"
 | 
|  #include "base/win/pe_image.h"
 | 
|  #include "base/win/scoped_handle.h"
 | 
| @@ -18,38 +21,7 @@ namespace base {
 | 
|  
 | 
|  namespace {
 | 
|  
 | 
| -class NativeStackSamplerWin : public StackSamplingProfiler::NativeStackSampler {
 | 
| - public:
 | 
| -  explicit NativeStackSamplerWin(win::ScopedHandle thread_handle);
 | 
| -  ~NativeStackSamplerWin() override;
 | 
| -
 | 
| -  // StackSamplingProfiler::NativeStackSampler:
 | 
| -  void ProfileRecordingStarting(
 | 
| -      StackSamplingProfiler::Profile* profile) override;
 | 
| -  void RecordStackSample(StackSamplingProfiler::Sample* sample) override;
 | 
| -  void ProfileRecordingStopped() override;
 | 
| -
 | 
| - private:
 | 
| -  static bool GetModuleInfo(HMODULE module,
 | 
| -                            StackSamplingProfiler::Module* module_info);
 | 
| -
 | 
| -  void CopyToSample(const void* const instruction_pointers[],
 | 
| -                    const HMODULE modules[],
 | 
| -                    int stack_depth,
 | 
| -                    StackSamplingProfiler::Sample* sample,
 | 
| -                    std::vector<StackSamplingProfiler::Module>* module_infos);
 | 
| -
 | 
| -  win::ScopedHandle thread_handle_;
 | 
| -  // Weak. Points to the profile being recorded between
 | 
| -  // ProfileRecordingStarting() and ProfileRecordingStopped().
 | 
| -  StackSamplingProfiler::Profile* current_profile_;
 | 
| -  // Maps a module to the module's index within current_profile_->modules.
 | 
| -  std::map<HMODULE, int> profile_module_index_;
 | 
| -
 | 
| -  DISALLOW_COPY_AND_ASSIGN(NativeStackSamplerWin);
 | 
| -};
 | 
| -
 | 
| -// Walk the stack represented by |context| from the current frame downwards,
 | 
| +// Walks the stack represented by |context| from the current frame downwards,
 | 
|  // recording the instruction pointers for each frame in |instruction_pointers|.
 | 
|  int RecordStack(CONTEXT* context,
 | 
|                  int max_stack_size,
 | 
| @@ -58,69 +30,104 @@ int RecordStack(CONTEXT* context,
 | 
|  #ifdef _WIN64
 | 
|    *last_frame_is_unknown_function = false;
 | 
|  
 | 
| -  IMAGEHLP_SYMBOL64 sym;
 | 
| -  sym.SizeOfStruct = sizeof(sym);
 | 
| -  sym.MaxNameLength = 0;
 | 
| -
 | 
| -  for (int i = 0; i < max_stack_size; ++i) {
 | 
| +  int i = 0;
 | 
| +  for (; (i < max_stack_size) && context->Rip; ++i) {
 | 
|      // Try to look up unwind metadata for the current function.
 | 
|      ULONG64 image_base;
 | 
|      PRUNTIME_FUNCTION runtime_function =
 | 
|          RtlLookupFunctionEntry(context->Rip, &image_base, nullptr);
 | 
|  
 | 
| -    instruction_pointers[i] = reinterpret_cast<void*>(context->Rip);
 | 
| +    instruction_pointers[i] = reinterpret_cast<const void*>(context->Rip);
 | 
|  
 | 
|      if (runtime_function) {
 | 
|        KNONVOLATILE_CONTEXT_POINTERS nvcontext = {0};
 | 
|        void* handler_data;
 | 
|        ULONG64 establisher_frame;
 | 
|        RtlVirtualUnwind(0, image_base, context->Rip, runtime_function, context,
 | 
| -          &handler_data, &establisher_frame, &nvcontext);
 | 
| +                       &handler_data, &establisher_frame, &nvcontext);
 | 
|      } else {
 | 
| -      // If we don't have a RUNTIME_FUNCTION, then we've encountered
 | 
| -      // a leaf function.  Adjust the stack appropriately.
 | 
| -      context->Rip = *reinterpret_cast<PDWORD64>(context->Rsp);
 | 
| -      context->Rsp += 8;
 | 
| -      *last_frame_is_unknown_function = true;
 | 
| -    }
 | 
| -
 | 
| -    if (!context->Rip)
 | 
| +      // If we don't have a RUNTIME_FUNCTION, then in theory this should be a
 | 
| +      // leaf function whose frame contains only a return address, at
 | 
| +      // RSP. However, crash data also indicates that some third party libraries
 | 
| +      // do not provide RUNTIME_FUNCTION information for non-leaf functions. We
 | 
| +      // could manually unwind the stack in the former case, but attempting to
 | 
| +      // do so in the latter case would produce wrong results and likely crash,
 | 
| +      // so just bail out.
 | 
| +      //
 | 
| +      // Ad hoc runs with instrumentation show that ~5% of stack traces end with
 | 
| +      // a valid leaf function. To avoid selectively omitting these traces it
 | 
| +      // makes sense to ultimately try to distinguish these two cases and
 | 
| +      // selectively unwind the stack for legitimate leaf functions. For the
 | 
| +      // purposes of avoiding crashes though, just ignore them all for now.
 | 
|        return i;
 | 
| +    }
 | 
|    }
 | 
| -  return max_stack_size;
 | 
| +  return i;
 | 
|  #else
 | 
|    return 0;
 | 
|  #endif
 | 
|  }
 | 
|  
 | 
| -// Fills in |modules| corresponding to the pointers to code in |addresses|. The
 | 
| -// modules are returned with reference counts incremented should be freed with
 | 
| -// FreeModules.
 | 
| -void FindModulesForAddresses(const void* const addresses[], HMODULE modules[],
 | 
| -                             int stack_depth,
 | 
| +// Fills in |module_handles| corresponding to the pointers to code in
 | 
| +// |addresses|. The module handles are returned with reference counts
 | 
| +// incremented and should be freed with FreeModuleHandles. See note in
 | 
| +// SuspendThreadAndRecordStack for why |addresses| and |module_handles| are
 | 
| +// arrays.
 | 
| +void FindModuleHandlesForAddresses(const void* const addresses[],
 | 
| +                             HMODULE module_handles[], int stack_depth,
 | 
|                               bool last_frame_is_unknown_function) {
 | 
| -  const int module_frames = last_frame_is_unknown_function ? stack_depth - 1 :
 | 
| -      stack_depth;
 | 
| +  const int module_frames =
 | 
| +      last_frame_is_unknown_function ? stack_depth - 1 : stack_depth;
 | 
|    for (int i = 0; i < module_frames; ++i) {
 | 
| -    HMODULE module = NULL;
 | 
| +    HMODULE module_handle = NULL;
 | 
|      if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
 | 
|                            reinterpret_cast<LPCTSTR>(addresses[i]),
 | 
| -                          &module)) {
 | 
| -      // HMODULE is the base address of the module.
 | 
| -      DCHECK_LT(reinterpret_cast<const void*>(module), addresses[i]);
 | 
| -      modules[i] = module;
 | 
| +                          &module_handle)) {
 | 
| +      // HMODULE actually represents the base address of the module, so we can
 | 
| +      // use it directly as an address.
 | 
| +      DCHECK_LE(reinterpret_cast<const void*>(module_handle), addresses[i]);
 | 
| +      module_handles[i] = module_handle;
 | 
|      }
 | 
|    }
 | 
|  }
 | 
|  
 | 
| -// Free the modules returned by FindModulesForAddresses.
 | 
| -void FreeModules(int stack_depth, HMODULE modules[]) {
 | 
| +// Frees the modules handles returned by FindModuleHandlesForAddresses. See note
 | 
| +// in SuspendThreadAndRecordStack for why |module_handles| is an array.
 | 
| +void FreeModuleHandles(int stack_depth, HMODULE module_handles[]) {
 | 
|    for (int i = 0; i < stack_depth; ++i) {
 | 
| -    if (modules[i])
 | 
| -      ::FreeLibrary(modules[i]);
 | 
| +    if (module_handles[i])
 | 
| +      ::FreeLibrary(module_handles[i]);
 | 
|    }
 | 
|  }
 | 
|  
 | 
| +// Gets the unique build ID for a module. Windows build IDs are created by a
 | 
| +// concatenation of a GUID and AGE fields found in the headers of a module. The
 | 
| +// GUID is stored in the first 16 bytes and the AGE is stored in the last 4
 | 
| +// bytes. Returns the empty string if the function fails to get the build ID.
 | 
| +//
 | 
| +// Example:
 | 
| +// dumpbin chrome.exe /headers | find "Format:"
 | 
| +//   ... Format: RSDS, {16B2A428-1DED-442E-9A36-FCE8CBD29726}, 10, ...
 | 
| +//
 | 
| +// The resulting buildID string of this instance of chrome.exe is
 | 
| +// "16B2A4281DED442E9A36FCE8CBD2972610".
 | 
| +//
 | 
| +// Note that the AGE field is encoded in decimal, not hex.
 | 
| +std::string GetBuildIDForModule(HMODULE module_handle) {
 | 
| +  GUID guid;
 | 
| +  DWORD age;
 | 
| +  win::PEImage(module_handle).GetDebugId(&guid, &age);
 | 
| +  const int kGUIDSize = 39;
 | 
| +  std::wstring build_id;
 | 
| +  int result =
 | 
| +      ::StringFromGUID2(guid, WriteInto(&build_id, kGUIDSize), kGUIDSize);
 | 
| +  if (result != kGUIDSize)
 | 
| +    return std::string();
 | 
| +  RemoveChars(build_id, L"{}-", &build_id);
 | 
| +  build_id += StringPrintf(L"%d", age);
 | 
| +  return WideToUTF8(build_id);
 | 
| +}
 | 
| +
 | 
|  // Disables priority boost on a thread for the lifetime of the object.
 | 
|  class ScopedDisablePriorityBoost {
 | 
|   public:
 | 
| @@ -141,185 +148,210 @@ ScopedDisablePriorityBoost::ScopedDisablePriorityBoost(HANDLE thread_handle)
 | 
|        boost_state_was_disabled_(false) {
 | 
|    got_previous_boost_state_ =
 | 
|        ::GetThreadPriorityBoost(thread_handle_, &boost_state_was_disabled_);
 | 
| -  if (got_previous_boost_state_ && !boost_state_was_disabled_) {
 | 
| -    // Confusingly, TRUE disables priority boost ...
 | 
| +  if (got_previous_boost_state_) {
 | 
| +    // Confusingly, TRUE disables priority boost.
 | 
|      ::SetThreadPriorityBoost(thread_handle_, TRUE);
 | 
|    }
 | 
|  }
 | 
|  
 | 
|  ScopedDisablePriorityBoost::~ScopedDisablePriorityBoost() {
 | 
| -  if (got_previous_boost_state_ && !boost_state_was_disabled_) {
 | 
| -    // ... and FALSE enables priority boost.
 | 
| -    ::SetThreadPriorityBoost(thread_handle_, FALSE);
 | 
| -  }
 | 
| +  if (got_previous_boost_state_)
 | 
| +    ::SetThreadPriorityBoost(thread_handle_, boost_state_was_disabled_);
 | 
|  }
 | 
|  
 | 
|  // Suspends the thread with |thread_handle|, records the stack into
 | 
|  // |instruction_pointers|, then resumes the thread. Returns the size of the
 | 
|  // stack.
 | 
| +//
 | 
| +// IMPORTANT NOTE: No heap allocations may occur between SuspendThread and
 | 
| +// ResumeThread. Otherwise this code can deadlock on heap locks acquired by the
 | 
| +// target thread before it was suspended. This is why we pass instruction
 | 
| +// pointers and module handles as preallocated arrays rather than vectors, since
 | 
| +// vectors make it too easy to subtly allocate memory.
 | 
|  int SuspendThreadAndRecordStack(HANDLE thread_handle, int max_stack_size,
 | 
|                                  const void* instruction_pointers[],
 | 
|                                  bool* last_frame_is_unknown_function) {
 | 
| -#if defined(_WIN64)
 | 
| -  if (RtlVirtualUnwind == nullptr || RtlLookupFunctionEntry == nullptr)
 | 
| -    return 0;
 | 
| -#endif
 | 
| -
 | 
| -  if (::SuspendThread(thread_handle) == -1) {
 | 
| -    LOG(ERROR) << "SuspendThread failed: " << GetLastError();
 | 
| +  if (::SuspendThread(thread_handle) == -1)
 | 
|      return 0;
 | 
| -  }
 | 
|  
 | 
| +  int stack_depth = 0;
 | 
|    CONTEXT thread_context = {0};
 | 
|    thread_context.ContextFlags = CONTEXT_FULL;
 | 
| -  if (!::GetThreadContext(thread_handle, &thread_context)) {
 | 
| -    LOG(ERROR) << "GetThreadContext failed: " << GetLastError();
 | 
| +  if (::GetThreadContext(thread_handle, &thread_context)) {
 | 
| +    stack_depth = RecordStack(&thread_context, max_stack_size,
 | 
| +                              instruction_pointers,
 | 
| +                              last_frame_is_unknown_function);
 | 
|    }
 | 
|  
 | 
| -  int stack_depth = RecordStack(&thread_context, max_stack_size,
 | 
| -                                instruction_pointers,
 | 
| -                                last_frame_is_unknown_function);
 | 
| -
 | 
| -  {
 | 
| -    ScopedDisablePriorityBoost disable_priority_boost(thread_handle);
 | 
| -    if (::ResumeThread(thread_handle) == -1)
 | 
| -      LOG(ERROR) << "ResumeThread failed: " << GetLastError();
 | 
| -  }
 | 
| +  // Disable the priority boost that the thread would otherwise receive on
 | 
| +  // resume. We do this to avoid artificially altering the dynamics of the
 | 
| +  // executing application any more than we already are by suspending and
 | 
| +  // resuming the thread.
 | 
| +  //
 | 
| +  // Note that this can racily disable a priority boost that otherwise would
 | 
| +  // have been given to the thread, if the thread is waiting on other wait
 | 
| +  // conditions at the time of SuspendThread and those conditions are satisfied
 | 
| +  // before priority boost is reenabled. The measured length of this window is
 | 
| +  // ~100us, so this should occur fairly rarely.
 | 
| +  ScopedDisablePriorityBoost disable_priority_boost(thread_handle);
 | 
| +  bool resume_thread_succeeded = ::ResumeThread(thread_handle) != -1;
 | 
| +  CHECK(resume_thread_succeeded) << "ResumeThread failed: " << GetLastError();
 | 
|  
 | 
|    return stack_depth;
 | 
|  }
 | 
|  
 | 
| -}  // namespace
 | 
| +class NativeStackSamplerWin : public NativeStackSampler {
 | 
| + public:
 | 
| +  explicit NativeStackSamplerWin(win::ScopedHandle thread_handle);
 | 
| +  ~NativeStackSamplerWin() override;
 | 
|  
 | 
| -scoped_ptr<StackSamplingProfiler::NativeStackSampler>
 | 
| -StackSamplingProfiler::NativeStackSampler::Create(PlatformThreadId thread_id) {
 | 
| -#if _WIN64
 | 
| -  // Get the thread's handle.
 | 
| -  HANDLE thread_handle = ::OpenThread(
 | 
| -      THREAD_GET_CONTEXT | THREAD_SUSPEND_RESUME | THREAD_QUERY_INFORMATION,
 | 
| -      FALSE,
 | 
| -      thread_id);
 | 
| -  DCHECK(thread_handle) << "OpenThread failed";
 | 
| +  // StackSamplingProfiler::NativeStackSampler:
 | 
| +  void ProfileRecordingStarting(
 | 
| +      std::vector<StackSamplingProfiler::Module>* modules) override;
 | 
| +  void RecordStackSample(StackSamplingProfiler::Sample* sample) override;
 | 
| +  void ProfileRecordingStopped() override;
 | 
|  
 | 
| -  return scoped_ptr<NativeStackSampler>(new NativeStackSamplerWin(
 | 
| -      win::ScopedHandle(thread_handle)));
 | 
| -#else
 | 
| -  return scoped_ptr<NativeStackSampler>();
 | 
| -#endif
 | 
| -}
 | 
| + private:
 | 
| +  // Attempts to query the module filename, base address, and id for
 | 
| +  // |module_handle|, and store them in |module|. Returns true if it succeeded.
 | 
| +  static bool GetModuleForHandle(HMODULE module_handle,
 | 
| +                                 StackSamplingProfiler::Module* module);
 | 
| +
 | 
| +  // Gets the index for the Module corresponding to |module_handle| in
 | 
| +  // |modules|, adding it if it's not already present. Returns
 | 
| +  // StackSamplingProfiler::Frame::kUnknownModuleIndex if no Module can be
 | 
| +  // determined for |module|.
 | 
| +  size_t GetModuleIndex(HMODULE module_handle,
 | 
| +                        std::vector<StackSamplingProfiler::Module>* modules);
 | 
| +
 | 
| +  // Copies the stack information represented by |instruction_pointers| into
 | 
| +  // |sample| and |modules|.
 | 
| +  void CopyToSample(const void* const instruction_pointers[],
 | 
| +                    const HMODULE module_handles[],
 | 
| +                    int stack_depth,
 | 
| +                    StackSamplingProfiler::Sample* sample,
 | 
| +                    std::vector<StackSamplingProfiler::Module>* modules);
 | 
| +
 | 
| +  win::ScopedHandle thread_handle_;
 | 
| +  // Weak. Points to the modules associated with the profile being recorded
 | 
| +  // between ProfileRecordingStarting() and ProfileRecordingStopped().
 | 
| +  std::vector<StackSamplingProfiler::Module>* current_modules_;
 | 
| +  // Maps a module handle to the corresponding Module's index within
 | 
| +  // current_modules_.
 | 
| +  std::map<HMODULE, size_t> profile_module_index_;
 | 
| +
 | 
| +  DISALLOW_COPY_AND_ASSIGN(NativeStackSamplerWin);
 | 
| +};
 | 
|  
 | 
|  NativeStackSamplerWin::NativeStackSamplerWin(win::ScopedHandle thread_handle)
 | 
|      : thread_handle_(thread_handle.Take()) {
 | 
| -#ifdef _WIN64
 | 
| -  if (RtlVirtualUnwind == nullptr && RtlLookupFunctionEntry == nullptr) {
 | 
| -    const HMODULE nt_dll_handle = ::GetModuleHandle(L"ntdll.dll");
 | 
| -    // This should always be non-null, but handle just in case.
 | 
| -    if (nt_dll_handle) {
 | 
| -      reinterpret_cast<void*&>(RtlVirtualUnwind) =
 | 
| -          ::GetProcAddress(nt_dll_handle, "RtlVirtualUnwind");
 | 
| -      reinterpret_cast<void*&>(RtlLookupFunctionEntry) =
 | 
| -          ::GetProcAddress(nt_dll_handle, "RtlLookupFunctionEntry");
 | 
| -    }
 | 
| -  }
 | 
| -#endif
 | 
|  }
 | 
|  
 | 
|  NativeStackSamplerWin::~NativeStackSamplerWin() {
 | 
|  }
 | 
|  
 | 
|  void NativeStackSamplerWin::ProfileRecordingStarting(
 | 
| -    StackSamplingProfiler::Profile* profile) {
 | 
| -  current_profile_ = profile;
 | 
| +    std::vector<StackSamplingProfiler::Module>* modules) {
 | 
| +  current_modules_ = modules;
 | 
|    profile_module_index_.clear();
 | 
|  }
 | 
|  
 | 
|  void NativeStackSamplerWin::RecordStackSample(
 | 
|      StackSamplingProfiler::Sample* sample) {
 | 
| -  DCHECK(current_profile_);
 | 
| +  DCHECK(current_modules_);
 | 
|  
 | 
|    const int max_stack_size = 64;
 | 
|    const void* instruction_pointers[max_stack_size] = {0};
 | 
| -  HMODULE modules[max_stack_size] = {0};
 | 
| +  HMODULE module_handles[max_stack_size] = {0};
 | 
|  
 | 
|    bool last_frame_is_unknown_function = false;
 | 
|    int stack_depth = SuspendThreadAndRecordStack(
 | 
|        thread_handle_.Get(), max_stack_size, instruction_pointers,
 | 
|        &last_frame_is_unknown_function);
 | 
| -  FindModulesForAddresses(instruction_pointers, modules, stack_depth,
 | 
| -                          last_frame_is_unknown_function);
 | 
| -  CopyToSample(instruction_pointers, modules, stack_depth, sample,
 | 
| -               ¤t_profile_->modules);
 | 
| -  FreeModules(stack_depth, modules);
 | 
| +  FindModuleHandlesForAddresses(instruction_pointers, module_handles,
 | 
| +                                stack_depth, last_frame_is_unknown_function);
 | 
| +  CopyToSample(instruction_pointers, module_handles, stack_depth, sample,
 | 
| +               current_modules_);
 | 
| +  FreeModuleHandles(stack_depth, module_handles);
 | 
|  }
 | 
|  
 | 
|  void NativeStackSamplerWin::ProfileRecordingStopped() {
 | 
| -  current_profile_ = nullptr;
 | 
| +  current_modules_ = nullptr;
 | 
|  }
 | 
|  
 | 
|  // static
 | 
| -bool NativeStackSamplerWin::GetModuleInfo(
 | 
| -    HMODULE module,
 | 
| -    StackSamplingProfiler::Module* module_info) {
 | 
| +bool NativeStackSamplerWin::GetModuleForHandle(
 | 
| +    HMODULE module_handle,
 | 
| +    StackSamplingProfiler::Module* module) {
 | 
|    wchar_t module_name[MAX_PATH];
 | 
|    DWORD result_length =
 | 
| -      GetModuleFileName(module, module_name, arraysize(module_name));
 | 
| +      GetModuleFileName(module_handle, module_name, arraysize(module_name));
 | 
|    if (result_length == 0)
 | 
|      return false;
 | 
|  
 | 
| -  module_info->filename = base::FilePath(module_name);
 | 
| +  module->filename = base::FilePath(module_name);
 | 
|  
 | 
| -  module_info->base_address = reinterpret_cast<const void*>(module);
 | 
| +  module->base_address = reinterpret_cast<const void*>(module_handle);
 | 
|  
 | 
| -  GUID guid;
 | 
| -  DWORD age;
 | 
| -  win::PEImage(module).GetDebugId(&guid, &age);
 | 
| -  module_info->id.insert(module_info->id.end(),
 | 
| -                         reinterpret_cast<char*>(&guid),
 | 
| -                         reinterpret_cast<char*>(&guid + 1));
 | 
| -  module_info->id.insert(module_info->id.end(),
 | 
| -                         reinterpret_cast<char*>(&age),
 | 
| -                         reinterpret_cast<char*>(&age + 1));
 | 
| +  module->id = GetBuildIDForModule(module_handle);
 | 
| +  if (module->id.empty())
 | 
| +    return false;
 | 
|  
 | 
|    return true;
 | 
|  }
 | 
|  
 | 
| +size_t NativeStackSamplerWin::GetModuleIndex(
 | 
| +    HMODULE module_handle,
 | 
| +    std::vector<StackSamplingProfiler::Module>* modules) {
 | 
| +  if (!module_handle)
 | 
| +    return StackSamplingProfiler::Frame::kUnknownModuleIndex;
 | 
| +
 | 
| +  auto loc = profile_module_index_.find(module_handle);
 | 
| +  if (loc == profile_module_index_.end()) {
 | 
| +    StackSamplingProfiler::Module module;
 | 
| +    if (!GetModuleForHandle(module_handle, &module))
 | 
| +      return StackSamplingProfiler::Frame::kUnknownModuleIndex;
 | 
| +    modules->push_back(module);
 | 
| +    loc = profile_module_index_.insert(std::make_pair(
 | 
| +        module_handle, modules->size() - 1)).first;
 | 
| +  }
 | 
| +
 | 
| +  return loc->second;
 | 
| +}
 | 
| +
 | 
|  void NativeStackSamplerWin::CopyToSample(
 | 
|      const void* const instruction_pointers[],
 | 
| -    const HMODULE modules[],
 | 
| +    const HMODULE module_handles[],
 | 
|      int stack_depth,
 | 
|      StackSamplingProfiler::Sample* sample,
 | 
| -    std::vector<StackSamplingProfiler::Module>* module_infos) {
 | 
| +    std::vector<StackSamplingProfiler::Module>* module) {
 | 
|    sample->clear();
 | 
|    sample->reserve(stack_depth);
 | 
|  
 | 
|    for (int i = 0; i < stack_depth; ++i) {
 | 
| -    sample->push_back(StackSamplingProfiler::Frame());
 | 
| -    StackSamplingProfiler::Frame& frame = sample->back();
 | 
| -
 | 
| -    frame.instruction_pointer = instruction_pointers[i];
 | 
| +    sample->push_back(StackSamplingProfiler::Frame(
 | 
| +        instruction_pointers[i],
 | 
| +        GetModuleIndex(module_handles[i], module)));
 | 
| +  }
 | 
| +}
 | 
|  
 | 
| -    // Record an invalid module index if we don't have a valid module.
 | 
| -    if (!modules[i]) {
 | 
| -      frame.module_index = -1;
 | 
| -      continue;
 | 
| -    }
 | 
| +}  // namespace
 | 
|  
 | 
| -    auto loc = profile_module_index_.find(modules[i]);
 | 
| -    if (loc == profile_module_index_.end()) {
 | 
| -      StackSamplingProfiler::Module module_info;
 | 
| -      // Record an invalid module index if we have a module but can't find
 | 
| -      // information on it.
 | 
| -      if (!GetModuleInfo(modules[i], &module_info)) {
 | 
| -        frame.module_index = -1;
 | 
| -        continue;
 | 
| -      }
 | 
| -      module_infos->push_back(module_info);
 | 
| -      loc = profile_module_index_.insert(std::make_pair(
 | 
| -          modules[i], static_cast<int>(module_infos->size() - 1))).first;
 | 
| -    }
 | 
| +scoped_ptr<NativeStackSampler> NativeStackSampler::Create(
 | 
| +    PlatformThreadId thread_id) {
 | 
| +#if _WIN64
 | 
| +  // Get the thread's handle.
 | 
| +  HANDLE thread_handle = ::OpenThread(
 | 
| +      THREAD_GET_CONTEXT | THREAD_SUSPEND_RESUME | THREAD_QUERY_INFORMATION,
 | 
| +      FALSE,
 | 
| +      thread_id);
 | 
|  
 | 
| -    frame.module_index = loc->second;
 | 
| +  if (thread_handle) {
 | 
| +    return scoped_ptr<NativeStackSampler>(new NativeStackSamplerWin(
 | 
| +        win::ScopedHandle(thread_handle)));
 | 
|    }
 | 
| +#endif
 | 
| +  return scoped_ptr<NativeStackSampler>();
 | 
|  }
 | 
|  
 | 
|  }  // namespace base
 | 
| 
 |