| Index: base/trace_event/memory_dump_manager.cc
 | 
| diff --git a/base/trace_event/memory_dump_manager.cc b/base/trace_event/memory_dump_manager.cc
 | 
| index 0ec5d1983374538598af6422d2084385c47dcbaa..859e8e02dc75dfca8f5e5726c7db7f131d9dc34d 100644
 | 
| --- a/base/trace_event/memory_dump_manager.cc
 | 
| +++ b/base/trace_event/memory_dump_manager.cc
 | 
| @@ -9,41 +9,123 @@
 | 
|  #include "base/atomic_sequence_num.h"
 | 
|  #include "base/compiler_specific.h"
 | 
|  #include "base/trace_event/memory_dump_provider.h"
 | 
| +#include "base/trace_event/memory_dump_session_state.h"
 | 
|  #include "base/trace_event/process_memory_dump.h"
 | 
|  #include "base/trace_event/trace_event_argument.h"
 | 
|  
 | 
| +#if defined(OS_LINUX) || defined(OS_ANDROID)
 | 
| +#include "base/trace_event/malloc_dump_provider.h"
 | 
| +#include "base/trace_event/process_memory_maps_dump_provider.h"
 | 
| +#include "base/trace_event/process_memory_totals_dump_provider.h"
 | 
| +#elif defined(OS_WIN)
 | 
| +#include "base/trace_event/winheap_dump_provider_win.h"
 | 
| +#endif
 | 
| +
 | 
|  namespace base {
 | 
|  namespace trace_event {
 | 
|  
 | 
|  namespace {
 | 
|  
 | 
| +// TODO(primiano): this should be smarter and should do something similar to
 | 
| +// trace event synthetic delays.
 | 
| +const char kTraceCategory[] = TRACE_DISABLED_BY_DEFAULT("memory-infra");
 | 
| +
 | 
|  MemoryDumpManager* g_instance_for_testing = nullptr;
 | 
| +const int kDumpIntervalSeconds = 2;
 | 
|  const int kTraceEventNumArgs = 1;
 | 
|  const char* kTraceEventArgNames[] = {"dumps"};
 | 
|  const unsigned char kTraceEventArgTypes[] = {TRACE_VALUE_TYPE_CONVERTABLE};
 | 
|  StaticAtomicSequenceNumber g_next_guid;
 | 
|  
 | 
| -const char* DumpPointTypeToString(const DumpPointType& dump_point_type) {
 | 
| -  switch (dump_point_type) {
 | 
| -    case DumpPointType::TASK_BEGIN:
 | 
| +const char* MemoryDumpTypeToString(const MemoryDumpType& dump_type) {
 | 
| +  switch (dump_type) {
 | 
| +    case MemoryDumpType::TASK_BEGIN:
 | 
|        return "TASK_BEGIN";
 | 
| -    case DumpPointType::TASK_END:
 | 
| +    case MemoryDumpType::TASK_END:
 | 
|        return "TASK_END";
 | 
| -    case DumpPointType::PERIODIC_INTERVAL:
 | 
| +    case MemoryDumpType::PERIODIC_INTERVAL:
 | 
|        return "PERIODIC_INTERVAL";
 | 
| -    case DumpPointType::EXPLICITLY_TRIGGERED:
 | 
| +    case MemoryDumpType::EXPLICITLY_TRIGGERED:
 | 
|        return "EXPLICITLY_TRIGGERED";
 | 
|    }
 | 
|    NOTREACHED();
 | 
|    return "UNKNOWN";
 | 
|  }
 | 
|  
 | 
| +// Internal class used to hold details about ProcessMemoryDump requests for the
 | 
| +// current process.
 | 
| +// TODO(primiano): In the upcoming CLs, ProcessMemoryDump will become async.
 | 
| +// and this class will be used to convey more details across PostTask()s.
 | 
| +class ProcessMemoryDumpHolder
 | 
| +    : public RefCountedThreadSafe<ProcessMemoryDumpHolder> {
 | 
| + public:
 | 
| +  ProcessMemoryDumpHolder(
 | 
| +      MemoryDumpRequestArgs req_args,
 | 
| +      const scoped_refptr<MemoryDumpSessionState>& session_state,
 | 
| +      MemoryDumpCallback callback)
 | 
| +      : process_memory_dump(session_state),
 | 
| +        req_args(req_args),
 | 
| +        callback(callback),
 | 
| +        task_runner(MessageLoop::current()->task_runner()),
 | 
| +        num_pending_async_requests(0) {}
 | 
| +
 | 
| +  ProcessMemoryDump process_memory_dump;
 | 
| +  const MemoryDumpRequestArgs req_args;
 | 
| +
 | 
| +  // Callback passed to the initial call to CreateProcessDump().
 | 
| +  MemoryDumpCallback callback;
 | 
| +
 | 
| +  // Thread on which FinalizeDumpAndAddToTrace() should be called, which is the
 | 
| +  // same that invoked the initial CreateProcessDump().
 | 
| +  const scoped_refptr<SingleThreadTaskRunner> task_runner;
 | 
| +
 | 
| +  // Number of pending ContinueAsyncProcessDump() calls.
 | 
| +  int num_pending_async_requests;
 | 
| +
 | 
| + private:
 | 
| +  friend class RefCountedThreadSafe<ProcessMemoryDumpHolder>;
 | 
| +  virtual ~ProcessMemoryDumpHolder() {}
 | 
| +  DISALLOW_COPY_AND_ASSIGN(ProcessMemoryDumpHolder);
 | 
| +};
 | 
| +
 | 
| +void FinalizeDumpAndAddToTrace(
 | 
| +    const scoped_refptr<ProcessMemoryDumpHolder>& pmd_holder) {
 | 
| +  DCHECK_EQ(0, pmd_holder->num_pending_async_requests);
 | 
| +
 | 
| +  if (!pmd_holder->task_runner->BelongsToCurrentThread()) {
 | 
| +    pmd_holder->task_runner->PostTask(
 | 
| +        FROM_HERE, Bind(&FinalizeDumpAndAddToTrace, pmd_holder));
 | 
| +    return;
 | 
| +  }
 | 
| +
 | 
| +  scoped_refptr<ConvertableToTraceFormat> event_value(new TracedValue());
 | 
| +  pmd_holder->process_memory_dump.AsValueInto(
 | 
| +      static_cast<TracedValue*>(event_value.get()));
 | 
| +  const char* const event_name =
 | 
| +      MemoryDumpTypeToString(pmd_holder->req_args.dump_type);
 | 
| +
 | 
| +  TRACE_EVENT_API_ADD_TRACE_EVENT(
 | 
| +      TRACE_EVENT_PHASE_MEMORY_DUMP,
 | 
| +      TraceLog::GetCategoryGroupEnabled(kTraceCategory), event_name,
 | 
| +      pmd_holder->req_args.dump_guid, kTraceEventNumArgs, kTraceEventArgNames,
 | 
| +      kTraceEventArgTypes, nullptr /* arg_values */, &event_value,
 | 
| +      TRACE_EVENT_FLAG_HAS_ID);
 | 
| +
 | 
| +  if (!pmd_holder->callback.is_null()) {
 | 
| +    pmd_holder->callback.Run(pmd_holder->req_args.dump_guid, true);
 | 
| +    pmd_holder->callback.Reset();
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +void RequestPeriodicGlobalDump() {
 | 
| +  MemoryDumpManager::GetInstance()->RequestGlobalDump(
 | 
| +      MemoryDumpType::PERIODIC_INTERVAL);
 | 
| +}
 | 
| +
 | 
|  }  // namespace
 | 
|  
 | 
| -// TODO(primiano): this should be smarter and should do something similar to
 | 
| -// trace event synthetic delays.
 | 
| -const char MemoryDumpManager::kTraceCategory[] =
 | 
| -    TRACE_DISABLED_BY_DEFAULT("memory-dumps");
 | 
| +// static
 | 
| +const char* const MemoryDumpManager::kTraceCategoryForTesting = kTraceCategory;
 | 
|  
 | 
|  // static
 | 
|  MemoryDumpManager* MemoryDumpManager::GetInstance() {
 | 
| @@ -56,11 +138,17 @@ MemoryDumpManager* MemoryDumpManager::GetInstance() {
 | 
|  
 | 
|  // static
 | 
|  void MemoryDumpManager::SetInstanceForTesting(MemoryDumpManager* instance) {
 | 
| +  if (instance)
 | 
| +    instance->skip_core_dumpers_auto_registration_for_testing_ = true;
 | 
|    g_instance_for_testing = instance;
 | 
|  }
 | 
|  
 | 
|  MemoryDumpManager::MemoryDumpManager()
 | 
| -    : dump_provider_currently_active_(nullptr), memory_tracing_enabled_(0) {
 | 
| +    : dump_provider_currently_active_(nullptr),
 | 
| +      delegate_(nullptr),
 | 
| +      memory_tracing_enabled_(0),
 | 
| +      skip_core_dumpers_auto_registration_for_testing_(false) {
 | 
| +  g_next_guid.GetNext();  // Make sure that first guid is not zero.
 | 
|  }
 | 
|  
 | 
|  MemoryDumpManager::~MemoryDumpManager() {
 | 
| @@ -70,94 +158,176 @@ MemoryDumpManager::~MemoryDumpManager() {
 | 
|  void MemoryDumpManager::Initialize() {
 | 
|    TRACE_EVENT0(kTraceCategory, "init");  // Add to trace-viewer category list.
 | 
|    trace_event::TraceLog::GetInstance()->AddEnabledStateObserver(this);
 | 
| +
 | 
| +  if (skip_core_dumpers_auto_registration_for_testing_)
 | 
| +    return;
 | 
| +
 | 
| +#if defined(OS_LINUX) || defined(OS_ANDROID)
 | 
| +  // Enable the core dump providers.
 | 
| +  RegisterDumpProvider(ProcessMemoryTotalsDumpProvider::GetInstance());
 | 
| +  RegisterDumpProvider(ProcessMemoryMapsDumpProvider::GetInstance());
 | 
| +  RegisterDumpProvider(MallocDumpProvider::GetInstance());
 | 
| +#elif defined(OS_WIN)
 | 
| +  RegisterDumpProvider(WinHeapDumpProvider::GetInstance());
 | 
| +#endif
 | 
| +}
 | 
| +
 | 
| +void MemoryDumpManager::SetDelegate(MemoryDumpManagerDelegate* delegate) {
 | 
| +  AutoLock lock(lock_);
 | 
| +  DCHECK_EQ(static_cast<MemoryDumpManagerDelegate*>(nullptr), delegate_);
 | 
| +  delegate_ = delegate;
 | 
|  }
 | 
|  
 | 
|  void MemoryDumpManager::RegisterDumpProvider(MemoryDumpProvider* mdp) {
 | 
|    AutoLock lock(lock_);
 | 
| -  if (std::find(dump_providers_registered_.begin(),
 | 
| -                dump_providers_registered_.end(),
 | 
| -                mdp) != dump_providers_registered_.end()) {
 | 
| -    return;
 | 
| -  }
 | 
| -  dump_providers_registered_.push_back(mdp);
 | 
| +  dump_providers_registered_.insert(mdp);
 | 
|  }
 | 
|  
 | 
|  void MemoryDumpManager::UnregisterDumpProvider(MemoryDumpProvider* mdp) {
 | 
|    AutoLock lock(lock_);
 | 
|  
 | 
| -  // Remove from the registered providers list.
 | 
| -  auto it = std::find(dump_providers_registered_.begin(),
 | 
| -                      dump_providers_registered_.end(), mdp);
 | 
| -  if (it != dump_providers_registered_.end())
 | 
| -    dump_providers_registered_.erase(it);
 | 
| +  // Unregistration of a MemoryDumpProvider while tracing is ongoing is safe
 | 
| +  // only if the MDP has specified a thread affinity (via task_runner()) AND
 | 
| +  // the unregistration happens on the same thread (so the MDP cannot unregister
 | 
| +  // and DumpInto() at the same time).
 | 
| +  // Otherwise, it is not possible to guarantee that its unregistration is
 | 
| +  // race-free. If you hit this DCHECK, your MDP has a bug.
 | 
| +  DCHECK_IMPLIES(
 | 
| +      subtle::NoBarrier_Load(&memory_tracing_enabled_),
 | 
| +      mdp->task_runner() && mdp->task_runner()->BelongsToCurrentThread())
 | 
| +      << "The MemoryDumpProvider " << mdp->GetFriendlyName() << " attempted to "
 | 
| +      << "unregister itself in a racy way. Please file a crbug.";
 | 
|  
 | 
|    // Remove from the enabled providers list. This is to deal with the case that
 | 
|    // UnregisterDumpProvider is called while the trace is enabled.
 | 
| -  it = std::find(dump_providers_enabled_.begin(), dump_providers_enabled_.end(),
 | 
| -                 mdp);
 | 
| -  if (it != dump_providers_enabled_.end())
 | 
| -    dump_providers_enabled_.erase(it);
 | 
| +  dump_providers_enabled_.erase(mdp);
 | 
| +  dump_providers_registered_.erase(mdp);
 | 
|  }
 | 
|  
 | 
| -void MemoryDumpManager::RequestDumpPoint(DumpPointType dump_point_type) {
 | 
| -  // TODO(primiano): this will have more logic to coordinate dump points across
 | 
| -  // multiple processes via IPC. See crbug.com/462930.
 | 
| -
 | 
| +void MemoryDumpManager::RequestGlobalDump(
 | 
| +    MemoryDumpType dump_type,
 | 
| +    const MemoryDumpCallback& callback) {
 | 
|    // Bail out immediately if tracing is not enabled at all.
 | 
|    if (!UNLIKELY(subtle::NoBarrier_Load(&memory_tracing_enabled_)))
 | 
|      return;
 | 
|  
 | 
| -  // TODO(primiano): Make guid actually unique (cross-process) by hashing it
 | 
| -  // with the PID. See crbug.com/462931 for details.
 | 
| -  const uint64 guid = g_next_guid.GetNext();
 | 
| -  CreateLocalDumpPoint(dump_point_type, guid);
 | 
| +  const uint64 guid =
 | 
| +      TraceLog::GetInstance()->MangleEventId(g_next_guid.GetNext());
 | 
| +
 | 
| +  // The delegate_ is supposed to be thread safe, immutable and long lived.
 | 
| +  // No need to keep the lock after we ensure that a delegate has been set.
 | 
| +  MemoryDumpManagerDelegate* delegate;
 | 
| +  {
 | 
| +    AutoLock lock(lock_);
 | 
| +    delegate = delegate_;
 | 
| +  }
 | 
| +
 | 
| +  if (delegate) {
 | 
| +    // The delegate is in charge to coordinate the request among all the
 | 
| +    // processes and call the CreateLocalDumpPoint on the local process.
 | 
| +    MemoryDumpRequestArgs args = {guid, dump_type};
 | 
| +    delegate->RequestGlobalMemoryDump(args, callback);
 | 
| +  } else if (!callback.is_null()) {
 | 
| +    callback.Run(guid, false /* success */);
 | 
| +  }
 | 
|  }
 | 
|  
 | 
| -void MemoryDumpManager::BroadcastDumpRequest() {
 | 
| -  NOTREACHED();  // TODO(primiano): implement IPC synchronization.
 | 
| +void MemoryDumpManager::RequestGlobalDump(MemoryDumpType dump_type) {
 | 
| +  RequestGlobalDump(dump_type, MemoryDumpCallback());
 | 
|  }
 | 
|  
 | 
| -// Creates a dump point for the current process and appends it to the trace.
 | 
| -void MemoryDumpManager::CreateLocalDumpPoint(DumpPointType dump_point_type,
 | 
| -                                             uint64 guid) {
 | 
| +// Creates a memory dump for the current process and appends it to the trace.
 | 
| +void MemoryDumpManager::CreateProcessDump(const MemoryDumpRequestArgs& args,
 | 
| +                                          const MemoryDumpCallback& callback) {
 | 
| +  scoped_refptr<ProcessMemoryDumpHolder> pmd_holder(
 | 
| +      new ProcessMemoryDumpHolder(args, session_state_, callback));
 | 
| +  ProcessMemoryDump* pmd = &pmd_holder->process_memory_dump;
 | 
|    bool did_any_provider_dump = false;
 | 
| -  scoped_ptr<ProcessMemoryDump> pmd(new ProcessMemoryDump());
 | 
|  
 | 
| -  // Serialize dump point generation so that memory dump providers don't have to
 | 
| -  // deal with thread safety.
 | 
| +  // Iterate over the active dump providers and invoke DumpInto(pmd).
 | 
| +  // The MDM guarantees linearity (at most one MDP is active within one
 | 
| +  // process) and thread-safety (MDM enforces the right locking when entering /
 | 
| +  // leaving the MDP.DumpInto() call). This is to simplify the clients' design
 | 
| +  // and not let the MDPs worry about locking.
 | 
| +  // As regards thread affinity, depending on the MDP configuration (see
 | 
| +  // memory_dump_provider.h), the DumpInto() invocation can happen:
 | 
| +  //  - Synchronousy on the MDM thread, when MDP.task_runner() is not set.
 | 
| +  //  - Posted on MDP.task_runner(), when MDP.task_runner() is set.
 | 
|    {
 | 
|      AutoLock lock(lock_);
 | 
| -    for (auto it = dump_providers_enabled_.begin();
 | 
| -         it != dump_providers_enabled_.end();) {
 | 
| -      dump_provider_currently_active_ = *it;
 | 
| -      if (dump_provider_currently_active_->DumpInto(pmd.get())) {
 | 
| -        did_any_provider_dump = true;
 | 
| -        ++it;
 | 
| +    for (auto dump_provider_iter = dump_providers_enabled_.begin();
 | 
| +         dump_provider_iter != dump_providers_enabled_.end();) {
 | 
| +      // InvokeDumpProviderLocked will remove the MDP from the set if it fails.
 | 
| +      MemoryDumpProvider* mdp = *dump_provider_iter;
 | 
| +      ++dump_provider_iter;
 | 
| +      if (mdp->task_runner()) {
 | 
| +        // The DumpInto() call must be posted.
 | 
| +        bool did_post_async_task = mdp->task_runner()->PostTask(
 | 
| +            FROM_HERE, Bind(&MemoryDumpManager::ContinueAsyncProcessDump,
 | 
| +                            Unretained(this), Unretained(mdp), pmd_holder));
 | 
| +        // The thread underlying the TaskRunner might have gone away.
 | 
| +        if (did_post_async_task)
 | 
| +          ++pmd_holder->num_pending_async_requests;
 | 
|        } else {
 | 
| -        LOG(ERROR) << "The memory dumper "
 | 
| -                   << dump_provider_currently_active_->GetFriendlyName()
 | 
| -                   << " failed, possibly due to sandboxing (crbug.com/461788), "
 | 
| -                      "disabling it for current process. Try restarting chrome "
 | 
| -                      "with the --no-sandbox switch.";
 | 
| -        it = dump_providers_enabled_.erase(it);
 | 
| +        // Invoke the dump provider synchronously.
 | 
| +        did_any_provider_dump |= InvokeDumpProviderLocked(mdp, pmd);
 | 
|        }
 | 
| -      dump_provider_currently_active_ = nullptr;
 | 
|      }
 | 
| -  }
 | 
| +  }  // AutoLock
 | 
|  
 | 
| -  // Don't create a dump point if all the dumpers failed.
 | 
| -  if (!did_any_provider_dump)
 | 
| -    return;
 | 
| +  // If at least one synchronous provider did dump and there are no pending
 | 
| +  // asynchronous requests, add the dump to the trace and invoke the callback
 | 
| +  // straight away (FinalizeDumpAndAddToTrace() takes care of the callback).
 | 
| +  if (did_any_provider_dump && pmd_holder->num_pending_async_requests == 0)
 | 
| +    FinalizeDumpAndAddToTrace(pmd_holder);
 | 
| +}
 | 
|  
 | 
| -  scoped_refptr<ConvertableToTraceFormat> event_value(new TracedValue());
 | 
| -  pmd->AsValueInto(static_cast<TracedValue*>(event_value.get()));
 | 
| -  const char* const event_name = DumpPointTypeToString(dump_point_type);
 | 
| +// Invokes the MemoryDumpProvider.DumpInto(), taking care of the failsafe logic
 | 
| +// which disables the dumper when failing (crbug.com/461788).
 | 
| +bool MemoryDumpManager::InvokeDumpProviderLocked(MemoryDumpProvider* mdp,
 | 
| +                                                 ProcessMemoryDump* pmd) {
 | 
| +  lock_.AssertAcquired();
 | 
| +  dump_provider_currently_active_ = mdp;
 | 
| +  bool dump_successful = mdp->DumpInto(pmd);
 | 
| +  dump_provider_currently_active_ = nullptr;
 | 
| +  if (!dump_successful) {
 | 
| +    LOG(ERROR) << "The memory dumper " << mdp->GetFriendlyName()
 | 
| +               << " failed, possibly due to sandboxing (crbug.com/461788), "
 | 
| +                  "disabling it for current process. Try restarting chrome "
 | 
| +                  "with the --no-sandbox switch.";
 | 
| +    dump_providers_enabled_.erase(mdp);
 | 
| +  }
 | 
| +  return dump_successful;
 | 
| +}
 | 
|  
 | 
| -  TRACE_EVENT_API_ADD_TRACE_EVENT(
 | 
| -      TRACE_EVENT_PHASE_MEMORY_DUMP,
 | 
| -      TraceLog::GetCategoryGroupEnabled(kTraceCategory), event_name, guid,
 | 
| -      kTraceEventNumArgs, kTraceEventArgNames, kTraceEventArgTypes,
 | 
| -      NULL /* arg_values */, &event_value, TRACE_EVENT_FLAG_HAS_ID);
 | 
| +// This is posted to arbitrary threads as a continuation of CreateProcessDump(),
 | 
| +// when one or more MemoryDumpProvider(s) require the DumpInto() call to happen
 | 
| +// on a different thread.
 | 
| +void MemoryDumpManager::ContinueAsyncProcessDump(
 | 
| +    MemoryDumpProvider* mdp,
 | 
| +    scoped_refptr<ProcessMemoryDumpHolder> pmd_holder) {
 | 
| +  bool should_finalize_dump = false;
 | 
| +  {
 | 
| +    // The lock here is to guarantee that different asynchronous dumps on
 | 
| +    // different threads are still serialized, so that the MemoryDumpProvider
 | 
| +    // has a consistent view of the |pmd| argument passed.
 | 
| +    AutoLock lock(lock_);
 | 
| +    ProcessMemoryDump* pmd = &pmd_holder->process_memory_dump;
 | 
| +
 | 
| +    // Check if the MemoryDumpProvider is still there. It might have been
 | 
| +    // destroyed and unregistered while hopping threads.
 | 
| +    if (dump_providers_enabled_.count(mdp))
 | 
| +      InvokeDumpProviderLocked(mdp, pmd);
 | 
| +
 | 
| +    // Finalize the dump appending it to the trace if this was the last
 | 
| +    // asynchronous request pending.
 | 
| +    --pmd_holder->num_pending_async_requests;
 | 
| +    if (pmd_holder->num_pending_async_requests == 0)
 | 
| +      should_finalize_dump = true;
 | 
| +  }  // AutoLock(lock_)
 | 
| +
 | 
| +  if (should_finalize_dump)
 | 
| +    FinalizeDumpAndAddToTrace(pmd_holder);
 | 
|  }
 | 
|  
 | 
|  void MemoryDumpManager::OnTraceLogEnabled() {
 | 
| @@ -168,19 +338,36 @@ void MemoryDumpManager::OnTraceLogEnabled() {
 | 
|    TRACE_EVENT_CATEGORY_GROUP_ENABLED(kTraceCategory, &enabled);
 | 
|  
 | 
|    AutoLock lock(lock_);
 | 
| -  if (enabled) {
 | 
| -    dump_providers_enabled_.assign(dump_providers_registered_.begin(),
 | 
| -                                   dump_providers_registered_.end());
 | 
| -  } else {
 | 
| +
 | 
| +  // There is no point starting the tracing without a delegate.
 | 
| +  if (!enabled || !delegate_) {
 | 
|      dump_providers_enabled_.clear();
 | 
| +    return;
 | 
| +  }
 | 
| +
 | 
| +  // Merge the dictionary of allocator attributes from all dump providers
 | 
| +  // into the session state.
 | 
| +  session_state_ = new MemoryDumpSessionState();
 | 
| +  for (const MemoryDumpProvider* mdp : dump_providers_registered_) {
 | 
| +    session_state_->allocators_attributes_type_info.Update(
 | 
| +        mdp->allocator_attributes_type_info());
 | 
|    }
 | 
| +  dump_providers_enabled_ = dump_providers_registered_;
 | 
|    subtle::NoBarrier_Store(&memory_tracing_enabled_, 1);
 | 
| +
 | 
| +  if (delegate_->IsCoordinatorProcess()) {
 | 
| +    periodic_dump_timer_.Start(FROM_HERE,
 | 
| +                               TimeDelta::FromSeconds(kDumpIntervalSeconds),
 | 
| +                               base::Bind(&RequestPeriodicGlobalDump));
 | 
| +  }
 | 
|  }
 | 
|  
 | 
|  void MemoryDumpManager::OnTraceLogDisabled() {
 | 
|    AutoLock lock(lock_);
 | 
| +  periodic_dump_timer_.Stop();
 | 
|    dump_providers_enabled_.clear();
 | 
|    subtle::NoBarrier_Store(&memory_tracing_enabled_, 0);
 | 
| +  session_state_ = nullptr;
 | 
|  }
 | 
|  
 | 
|  }  // namespace trace_event
 | 
| 
 |