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 "base/trace_event/memory_dump_manager.h"
6
7#include <algorithm>
8#include <utility>
9
10#include "base/atomic_sequence_num.h"
11#include "base/base_switches.h"
12#include "base/command_line.h"
13#include "base/compiler_specific.h"
14#include "base/thread_task_runner_handle.h"
15#include "base/threading/thread.h"
16#include "base/trace_event/heap_profiler_allocation_context_tracker.h"
17#include "base/trace_event/heap_profiler_stack_frame_deduplicator.h"
18#include "base/trace_event/heap_profiler_type_name_deduplicator.h"
19#include "base/trace_event/malloc_dump_provider.h"
20#include "base/trace_event/memory_dump_provider.h"
21#include "base/trace_event/memory_dump_session_state.h"
22#include "base/trace_event/process_memory_dump.h"
23#include "base/trace_event/trace_event_argument.h"
24#include "build/build_config.h"
25
26#if !defined(OS_NACL)
27#include "base/trace_event/process_memory_totals_dump_provider.h"
28#endif
29
30#if defined(OS_LINUX) || defined(OS_ANDROID)
31#include "base/trace_event/process_memory_maps_dump_provider.h"
32#endif
33
34#if defined(OS_ANDROID)
35#include "base/trace_event/java_heap_dump_provider_android.h"
36#endif
37
38#if defined(OS_WIN)
39#include "base/trace_event/winheap_dump_provider_win.h"
40#endif
41
42namespace base {
43namespace trace_event {
44
45namespace {
46
47const int kTraceEventNumArgs = 1;
48const char* kTraceEventArgNames[] = {"dumps"};
49const unsigned char kTraceEventArgTypes[] = {TRACE_VALUE_TYPE_CONVERTABLE};
50
51StaticAtomicSequenceNumber g_next_guid;
52uint32_t g_periodic_dumps_count = 0;
53uint32_t g_heavy_dumps_rate = 0;
54MemoryDumpManager* g_instance_for_testing = nullptr;
55
56void RequestPeriodicGlobalDump() {
57  MemoryDumpLevelOfDetail level_of_detail;
58  if (g_heavy_dumps_rate == 0) {
59    level_of_detail = MemoryDumpLevelOfDetail::LIGHT;
60  } else {
61    level_of_detail = g_periodic_dumps_count == 0
62                          ? MemoryDumpLevelOfDetail::DETAILED
63                          : MemoryDumpLevelOfDetail::LIGHT;
64
65    if (++g_periodic_dumps_count == g_heavy_dumps_rate)
66      g_periodic_dumps_count = 0;
67  }
68
69  MemoryDumpManager::GetInstance()->RequestGlobalDump(
70      MemoryDumpType::PERIODIC_INTERVAL, level_of_detail);
71}
72
73// Callback wrapper to hook upon the completion of RequestGlobalDump() and
74// inject trace markers.
75void OnGlobalDumpDone(MemoryDumpCallback wrapped_callback,
76                      uint64_t dump_guid,
77                      bool success) {
78  TRACE_EVENT_NESTABLE_ASYNC_END1(
79      MemoryDumpManager::kTraceCategory, "GlobalMemoryDump",
80      TRACE_ID_MANGLE(dump_guid), "success", success);
81
82  if (!wrapped_callback.is_null()) {
83    wrapped_callback.Run(dump_guid, success);
84    wrapped_callback.Reset();
85  }
86}
87
88}  // namespace
89
90// static
91const char* const MemoryDumpManager::kTraceCategory =
92    TRACE_DISABLED_BY_DEFAULT("memory-infra");
93
94// static
95const int MemoryDumpManager::kMaxConsecutiveFailuresCount = 3;
96
97// static
98const uint64_t MemoryDumpManager::kInvalidTracingProcessId = 0;
99
100// static
101const char* const MemoryDumpManager::kSystemAllocatorPoolName =
102#if defined(MALLOC_MEMORY_TRACING_SUPPORTED)
103    MallocDumpProvider::kAllocatedObjects;
104#elif defined(OS_WIN)
105    WinHeapDumpProvider::kAllocatedObjects;
106#else
107    nullptr;
108#endif
109
110// static
111MemoryDumpManager* MemoryDumpManager::GetInstance() {
112  if (g_instance_for_testing)
113    return g_instance_for_testing;
114
115  return Singleton<MemoryDumpManager,
116                   LeakySingletonTraits<MemoryDumpManager>>::get();
117}
118
119// static
120void MemoryDumpManager::SetInstanceForTesting(MemoryDumpManager* instance) {
121  g_instance_for_testing = instance;
122}
123
124MemoryDumpManager::MemoryDumpManager()
125    : delegate_(nullptr),
126      is_coordinator_(false),
127      memory_tracing_enabled_(0),
128      tracing_process_id_(kInvalidTracingProcessId),
129      dumper_registrations_ignored_for_testing_(false) {
130  g_next_guid.GetNext();  // Make sure that first guid is not zero.
131
132  heap_profiling_enabled_ = CommandLine::InitializedForCurrentProcess()
133                                ? CommandLine::ForCurrentProcess()->HasSwitch(
134                                      switches::kEnableHeapProfiling)
135                                : false;
136
137  if (heap_profiling_enabled_)
138    AllocationContextTracker::SetCaptureEnabled(true);
139}
140
141MemoryDumpManager::~MemoryDumpManager() {
142  TraceLog::GetInstance()->RemoveEnabledStateObserver(this);
143}
144
145void MemoryDumpManager::Initialize(MemoryDumpManagerDelegate* delegate,
146                                   bool is_coordinator) {
147  {
148    AutoLock lock(lock_);
149    DCHECK(delegate);
150    DCHECK(!delegate_);
151    delegate_ = delegate;
152    is_coordinator_ = is_coordinator;
153  }
154
155// Enable the core dump providers.
156#if !defined(OS_NACL)
157  RegisterDumpProvider(ProcessMemoryTotalsDumpProvider::GetInstance(),
158                       "ProcessMemoryTotals", nullptr);
159#endif
160
161#if defined(MALLOC_MEMORY_TRACING_SUPPORTED)
162  RegisterDumpProvider(MallocDumpProvider::GetInstance(), "Malloc", nullptr);
163#endif
164
165#if defined(OS_LINUX) || defined(OS_ANDROID)
166  RegisterDumpProvider(ProcessMemoryMapsDumpProvider::GetInstance(),
167                       "ProcessMemoryMaps", nullptr);
168#endif
169
170#if defined(OS_ANDROID)
171  RegisterDumpProvider(JavaHeapDumpProvider::GetInstance(), "JavaHeap",
172                       nullptr);
173#endif
174
175#if defined(OS_WIN)
176  RegisterDumpProvider(WinHeapDumpProvider::GetInstance(), "WinHeap", nullptr);
177#endif
178
179  // If tracing was enabled before initializing MemoryDumpManager, we missed the
180  // OnTraceLogEnabled() event. Synthetize it so we can late-join the party.
181  bool is_tracing_already_enabled = TraceLog::GetInstance()->IsEnabled();
182  TRACE_EVENT0(kTraceCategory, "init");  // Add to trace-viewer category list.
183  TraceLog::GetInstance()->AddEnabledStateObserver(this);
184  if (is_tracing_already_enabled)
185    OnTraceLogEnabled();
186}
187
188void MemoryDumpManager::RegisterDumpProvider(
189    MemoryDumpProvider* mdp,
190    const char* name,
191    const scoped_refptr<SingleThreadTaskRunner>& task_runner,
192    const MemoryDumpProvider::Options& options) {
193  if (dumper_registrations_ignored_for_testing_)
194    return;
195
196  scoped_refptr<MemoryDumpProviderInfo> mdpinfo =
197      new MemoryDumpProviderInfo(mdp, name, task_runner, options);
198
199  {
200    AutoLock lock(lock_);
201    bool already_registered = !dump_providers_.insert(mdpinfo).second;
202    // This actually happens in some tests which don't have a clean tear-down
203    // path for RenderThreadImpl::Init().
204    if (already_registered)
205      return;
206  }
207
208  if (heap_profiling_enabled_)
209    mdp->OnHeapProfilingEnabled(true);
210}
211
212void MemoryDumpManager::RegisterDumpProvider(
213    MemoryDumpProvider* mdp,
214    const char* name,
215    const scoped_refptr<SingleThreadTaskRunner>& task_runner) {
216  RegisterDumpProvider(mdp, name, task_runner, MemoryDumpProvider::Options());
217}
218
219void MemoryDumpManager::UnregisterDumpProvider(MemoryDumpProvider* mdp) {
220  UnregisterDumpProviderInternal(mdp, false /* delete_async */);
221}
222
223void MemoryDumpManager::UnregisterAndDeleteDumpProviderSoon(
224    scoped_ptr<MemoryDumpProvider> mdp) {
225  UnregisterDumpProviderInternal(mdp.release(), true /* delete_async */);
226}
227
228void MemoryDumpManager::UnregisterDumpProviderInternal(
229    MemoryDumpProvider* mdp,
230    bool take_mdp_ownership_and_delete_async) {
231  scoped_ptr<MemoryDumpProvider> owned_mdp;
232  if (take_mdp_ownership_and_delete_async)
233    owned_mdp.reset(mdp);
234
235  AutoLock lock(lock_);
236
237  auto mdp_iter = dump_providers_.begin();
238  for (; mdp_iter != dump_providers_.end(); ++mdp_iter) {
239    if ((*mdp_iter)->dump_provider == mdp)
240      break;
241  }
242
243  if (mdp_iter == dump_providers_.end())
244    return;  // Not registered / already unregistered.
245
246  if (take_mdp_ownership_and_delete_async) {
247    // The MDP will be deleted whenever the MDPInfo struct will, that is either:
248    // - At the end of this function, if no dump is in progress.
249    // - In the prologue of the ContinueAsyncProcessDump().
250    DCHECK(!(*mdp_iter)->owned_dump_provider);
251    (*mdp_iter)->owned_dump_provider = std::move(owned_mdp);
252  } else if (subtle::NoBarrier_Load(&memory_tracing_enabled_)) {
253    // If you hit this DCHECK, your dump provider has a bug.
254    // Unregistration of a MemoryDumpProvider is safe only if:
255    // - The MDP has specified a thread affinity (via task_runner()) AND
256    //   the unregistration happens on the same thread (so the MDP cannot
257    //   unregister and be in the middle of a OnMemoryDump() at the same time.
258    // - The MDP has NOT specified a thread affinity and its ownership is
259    //   transferred via UnregisterAndDeleteDumpProviderSoon().
260    // In all the other cases, it is not possible to guarantee that the
261    // unregistration will not race with OnMemoryDump() calls.
262    DCHECK((*mdp_iter)->task_runner &&
263           (*mdp_iter)->task_runner->BelongsToCurrentThread())
264        << "MemoryDumpProvider \"" << (*mdp_iter)->name << "\" attempted to "
265        << "unregister itself in a racy way. Please file a crbug.";
266  }
267
268  // The MDPInfo instance can still be referenced by the
269  // |ProcessMemoryDumpAsyncState.pending_dump_providers|. For this reason
270  // the MDPInfo is flagged as disabled. It will cause ContinueAsyncProcessDump
271  // to just skip it, without actually invoking the |mdp|, which might be
272  // destroyed by the caller soon after this method returns.
273  (*mdp_iter)->disabled = true;
274  dump_providers_.erase(mdp_iter);
275}
276
277void MemoryDumpManager::RequestGlobalDump(
278    MemoryDumpType dump_type,
279    MemoryDumpLevelOfDetail level_of_detail,
280    const MemoryDumpCallback& callback) {
281  // Bail out immediately if tracing is not enabled at all.
282  if (!UNLIKELY(subtle::NoBarrier_Load(&memory_tracing_enabled_))) {
283    if (!callback.is_null())
284      callback.Run(0u /* guid */, false /* success */);
285    return;
286  }
287
288  const uint64_t guid =
289      TraceLog::GetInstance()->MangleEventId(g_next_guid.GetNext());
290
291  // Creates an async event to keep track of the global dump evolution.
292  // The |wrapped_callback| will generate the ASYNC_END event and then invoke
293  // the real |callback| provided by the caller.
294  TRACE_EVENT_NESTABLE_ASYNC_BEGIN0(kTraceCategory, "GlobalMemoryDump",
295                                    TRACE_ID_MANGLE(guid));
296  MemoryDumpCallback wrapped_callback = Bind(&OnGlobalDumpDone, callback);
297
298  // Technically there is no need to grab the |lock_| here as the delegate is
299  // long-lived and can only be set by Initialize(), which is locked and
300  // necessarily happens before memory_tracing_enabled_ == true.
301  // Not taking the |lock_|, though, is lakely make TSan barf and, at this point
302  // (memory-infra is enabled) we're not in the fast-path anymore.
303  MemoryDumpManagerDelegate* delegate;
304  {
305    AutoLock lock(lock_);
306    delegate = delegate_;
307  }
308
309  // The delegate will coordinate the IPC broadcast and at some point invoke
310  // CreateProcessDump() to get a dump for the current process.
311  MemoryDumpRequestArgs args = {guid, dump_type, level_of_detail};
312  delegate->RequestGlobalMemoryDump(args, wrapped_callback);
313}
314
315void MemoryDumpManager::RequestGlobalDump(
316    MemoryDumpType dump_type,
317    MemoryDumpLevelOfDetail level_of_detail) {
318  RequestGlobalDump(dump_type, level_of_detail, MemoryDumpCallback());
319}
320
321void MemoryDumpManager::CreateProcessDump(const MemoryDumpRequestArgs& args,
322                                          const MemoryDumpCallback& callback) {
323  TRACE_EVENT_NESTABLE_ASYNC_BEGIN0(kTraceCategory, "ProcessMemoryDump",
324                                    TRACE_ID_MANGLE(args.dump_guid));
325
326  scoped_ptr<ProcessMemoryDumpAsyncState> pmd_async_state;
327  {
328    AutoLock lock(lock_);
329    pmd_async_state.reset(
330        new ProcessMemoryDumpAsyncState(args, dump_providers_, session_state_,
331                                        callback, dump_thread_->task_runner()));
332  }
333
334  TRACE_EVENT_WITH_FLOW0(kTraceCategory, "MemoryDumpManager::CreateProcessDump",
335                         TRACE_ID_MANGLE(args.dump_guid),
336                         TRACE_EVENT_FLAG_FLOW_OUT);
337
338  // Start the thread hop. |dump_providers_| are kept sorted by thread, so
339  // ContinueAsyncProcessDump will hop at most once per thread (w.r.t. thread
340  // affinity specified by the MemoryDumpProvider(s) in RegisterDumpProvider()).
341  ContinueAsyncProcessDump(pmd_async_state.release());
342}
343
344// At most one ContinueAsyncProcessDump() can be active at any time for a given
345// PMD, regardless of status of the |lock_|. |lock_| is used here purely to
346// ensure consistency w.r.t. (un)registrations of |dump_providers_|.
347// The linearization of dump providers' OnMemoryDump invocations is achieved by
348// means of subsequent PostTask(s).
349//
350// 1) Prologue:
351//   - If this was the last hop, create a trace event, add it to the trace
352//     and finalize (invoke callback).
353//   - Check if we are on the right thread. If not hop and continue there.
354//   - Check if the dump provider is disabled, if so skip the dump.
355// 2) Invoke the dump provider's OnMemoryDump() (unless skipped).
356// 3) Epilogue:
357//   - Unregister the dump provider if it failed too many times consecutively.
358//   - Pop() the MDP from the |pending_dump_providers| list, eventually
359//     destroying the MDPInfo if that was unregistered in the meantime.
360void MemoryDumpManager::ContinueAsyncProcessDump(
361    ProcessMemoryDumpAsyncState* owned_pmd_async_state) {
362  // Initalizes the ThreadLocalEventBuffer to guarantee that the TRACE_EVENTs
363  // in the PostTask below don't end up registering their own dump providers
364  // (for discounting trace memory overhead) while holding the |lock_|.
365  TraceLog::GetInstance()->InitializeThreadLocalEventBufferIfSupported();
366
367  // In theory |owned_pmd_async_state| should be a scoped_ptr. The only reason
368  // why it isn't is because of the corner case logic of |did_post_task| below,
369  // which needs to take back the ownership of the |pmd_async_state| when a
370  // thread goes away and consequently the PostTask() fails.
371  // Unfortunately, PostTask() destroys the scoped_ptr arguments upon failure
372  // to prevent accidental leaks. Using a scoped_ptr would prevent us to to
373  // skip the hop and move on. Hence the manual naked -> scoped ptr juggling.
374  auto pmd_async_state = make_scoped_ptr(owned_pmd_async_state);
375  owned_pmd_async_state = nullptr;
376
377  if (pmd_async_state->pending_dump_providers.empty())
378    return FinalizeDumpAndAddToTrace(std::move(pmd_async_state));
379
380  // Read MemoryDumpProviderInfo thread safety considerations in
381  // memory_dump_manager.h when accessing |mdpinfo| fields.
382  MemoryDumpProviderInfo* mdpinfo =
383      pmd_async_state->pending_dump_providers.back().get();
384
385  // If the dump provider did not specify a thread affinity, dump on
386  // |dump_thread_|. Note that |dump_thread_| might have been Stop()-ed at this
387  // point (if tracing was disabled in the meanwhile). In such case the
388  // PostTask() below will fail, but |task_runner| should always be non-null.
389  SingleThreadTaskRunner* task_runner = mdpinfo->task_runner.get();
390  if (!task_runner)
391    task_runner = pmd_async_state->dump_thread_task_runner.get();
392
393  bool post_task_failed = false;
394  if (!task_runner->BelongsToCurrentThread()) {
395    // It's time to hop onto another thread.
396    post_task_failed = !task_runner->PostTask(
397        FROM_HERE, Bind(&MemoryDumpManager::ContinueAsyncProcessDump,
398                        Unretained(this), Unretained(pmd_async_state.get())));
399    if (!post_task_failed) {
400      // Ownership is tranferred to the next ContinueAsyncProcessDump().
401      ignore_result(pmd_async_state.release());
402      return;
403    }
404  }
405
406  // At this point either:
407  // - The MDP has a task runner affinity and we are on the right thread.
408  // - The MDP has a task runner affinity but the underlying thread is gone,
409  //   hence the above |post_task_failed| == true.
410  // - The MDP does NOT have a task runner affinity. A locked access is required
411  //   to R/W |disabled| (for the UnregisterAndDeleteDumpProviderSoon() case).
412  bool should_dump;
413  const char* disabled_reason = nullptr;
414  {
415    AutoLock lock(lock_);
416    if (!mdpinfo->disabled) {
417      if (mdpinfo->consecutive_failures >= kMaxConsecutiveFailuresCount) {
418        mdpinfo->disabled = true;
419        disabled_reason =
420            "Dump failure, possibly related with sandboxing (crbug.com/461788)."
421            " Try --no-sandbox.";
422      } else if (post_task_failed) {
423        disabled_reason = "The thread it was meant to dump onto is gone.";
424        mdpinfo->disabled = true;
425      }
426    }
427    should_dump = !mdpinfo->disabled;
428  }
429
430  if (disabled_reason) {
431    LOG(ERROR) << "Disabling MemoryDumpProvider \"" << mdpinfo->name << "\". "
432               << disabled_reason;
433  }
434
435  if (should_dump) {
436    // Invoke the dump provider.
437    TRACE_EVENT_WITH_FLOW1(kTraceCategory,
438                           "MemoryDumpManager::ContinueAsyncProcessDump",
439                           TRACE_ID_MANGLE(pmd_async_state->req_args.dump_guid),
440                           TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT,
441                           "dump_provider.name", mdpinfo->name);
442
443    // Pid of the target process being dumped. Often kNullProcessId (= current
444    // process), non-zero when the coordinator process creates dumps on behalf
445    // of child processes (see crbug.com/461788).
446    ProcessId target_pid = mdpinfo->options.target_pid;
447    ProcessMemoryDump* pmd =
448        pmd_async_state->GetOrCreateMemoryDumpContainerForProcess(target_pid);
449    MemoryDumpArgs args = {pmd_async_state->req_args.level_of_detail};
450    bool dump_successful = mdpinfo->dump_provider->OnMemoryDump(args, pmd);
451    mdpinfo->consecutive_failures =
452        dump_successful ? 0 : mdpinfo->consecutive_failures + 1;
453  }  // if (!mdpinfo->disabled)
454
455  pmd_async_state->pending_dump_providers.pop_back();
456  ContinueAsyncProcessDump(pmd_async_state.release());
457}
458
459// static
460void MemoryDumpManager::FinalizeDumpAndAddToTrace(
461    scoped_ptr<ProcessMemoryDumpAsyncState> pmd_async_state) {
462  DCHECK(pmd_async_state->pending_dump_providers.empty());
463  const uint64_t dump_guid = pmd_async_state->req_args.dump_guid;
464  if (!pmd_async_state->callback_task_runner->BelongsToCurrentThread()) {
465    scoped_refptr<SingleThreadTaskRunner> callback_task_runner =
466        pmd_async_state->callback_task_runner;
467    callback_task_runner->PostTask(
468        FROM_HERE, Bind(&MemoryDumpManager::FinalizeDumpAndAddToTrace,
469                        Passed(&pmd_async_state)));
470    return;
471  }
472
473  TRACE_EVENT_WITH_FLOW0(kTraceCategory,
474                         "MemoryDumpManager::FinalizeDumpAndAddToTrace",
475                         TRACE_ID_MANGLE(dump_guid), TRACE_EVENT_FLAG_FLOW_IN);
476
477  for (const auto& kv : pmd_async_state->process_dumps) {
478    ProcessId pid = kv.first;  // kNullProcessId for the current process.
479    ProcessMemoryDump* process_memory_dump = kv.second.get();
480    TracedValue* traced_value = new TracedValue();
481    scoped_refptr<ConvertableToTraceFormat> event_value(traced_value);
482    process_memory_dump->AsValueInto(traced_value);
483    traced_value->SetString("level_of_detail",
484                            MemoryDumpLevelOfDetailToString(
485                                pmd_async_state->req_args.level_of_detail));
486    const char* const event_name =
487        MemoryDumpTypeToString(pmd_async_state->req_args.dump_type);
488
489    TRACE_EVENT_API_ADD_TRACE_EVENT_WITH_PROCESS_ID(
490        TRACE_EVENT_PHASE_MEMORY_DUMP,
491        TraceLog::GetCategoryGroupEnabled(kTraceCategory), event_name,
492        dump_guid, pid, kTraceEventNumArgs, kTraceEventArgNames,
493        kTraceEventArgTypes, nullptr /* arg_values */, &event_value,
494        TRACE_EVENT_FLAG_HAS_ID);
495  }
496
497  if (!pmd_async_state->callback.is_null()) {
498    pmd_async_state->callback.Run(dump_guid, true /* success */);
499    pmd_async_state->callback.Reset();
500  }
501
502  TRACE_EVENT_NESTABLE_ASYNC_END0(kTraceCategory, "ProcessMemoryDump",
503                                  TRACE_ID_MANGLE(dump_guid));
504}
505
506void MemoryDumpManager::OnTraceLogEnabled() {
507  bool enabled;
508  TRACE_EVENT_CATEGORY_GROUP_ENABLED(kTraceCategory, &enabled);
509  if (!enabled)
510    return;
511
512  // Initialize the TraceLog for the current thread. This is to avoid that the
513  // TraceLog memory dump provider is registered lazily in the PostTask() below
514  // while the |lock_| is taken;
515  TraceLog::GetInstance()->InitializeThreadLocalEventBufferIfSupported();
516
517  // Spin-up the thread used to invoke unbound dump providers.
518  scoped_ptr<Thread> dump_thread(new Thread("MemoryInfra"));
519  if (!dump_thread->Start()) {
520    LOG(ERROR) << "Failed to start the memory-infra thread for tracing";
521    return;
522  }
523
524  AutoLock lock(lock_);
525
526  DCHECK(delegate_);  // At this point we must have a delegate.
527
528  scoped_refptr<StackFrameDeduplicator> stack_frame_deduplicator = nullptr;
529  scoped_refptr<TypeNameDeduplicator> type_name_deduplicator = nullptr;
530
531  if (heap_profiling_enabled_) {
532    // If heap profiling is enabled, the stack frame deduplicator and type name
533    // deduplicator will be in use. Add a metadata events to write the frames
534    // and type IDs.
535    stack_frame_deduplicator = new StackFrameDeduplicator;
536    type_name_deduplicator = new TypeNameDeduplicator;
537    TRACE_EVENT_API_ADD_METADATA_EVENT(
538        "stackFrames", "stackFrames",
539        scoped_refptr<ConvertableToTraceFormat>(stack_frame_deduplicator));
540    TRACE_EVENT_API_ADD_METADATA_EVENT(
541        "typeNames", "typeNames",
542        scoped_refptr<ConvertableToTraceFormat>(type_name_deduplicator));
543  }
544
545  DCHECK(!dump_thread_);
546  dump_thread_ = std::move(dump_thread);
547  session_state_ = new MemoryDumpSessionState(stack_frame_deduplicator,
548                                              type_name_deduplicator);
549
550  subtle::NoBarrier_Store(&memory_tracing_enabled_, 1);
551
552  // TODO(primiano): This is a temporary hack to disable periodic memory dumps
553  // when running memory benchmarks until telemetry uses TraceConfig to
554  // enable/disable periodic dumps. See crbug.com/529184 .
555  if (!is_coordinator_ ||
556      CommandLine::ForCurrentProcess()->HasSwitch(
557          "enable-memory-benchmarking")) {
558    return;
559  }
560
561  // Enable periodic dumps. At the moment the periodic support is limited to at
562  // most one low-detail periodic dump and at most one high-detail periodic
563  // dump. If both are specified the high-detail period must be an integer
564  // multiple of the low-level one.
565  g_periodic_dumps_count = 0;
566  const TraceConfig trace_config =
567      TraceLog::GetInstance()->GetCurrentTraceConfig();
568  const TraceConfig::MemoryDumpConfig& config_list =
569      trace_config.memory_dump_config();
570  if (config_list.empty())
571    return;
572
573  uint32_t min_timer_period_ms = std::numeric_limits<uint32_t>::max();
574  uint32_t heavy_dump_period_ms = 0;
575  DCHECK_LE(config_list.size(), 2u);
576  for (const TraceConfig::MemoryDumpTriggerConfig& config : config_list) {
577    DCHECK(config.periodic_interval_ms);
578    if (config.level_of_detail == MemoryDumpLevelOfDetail::DETAILED)
579      heavy_dump_period_ms = config.periodic_interval_ms;
580    min_timer_period_ms =
581        std::min(min_timer_period_ms, config.periodic_interval_ms);
582  }
583  DCHECK_EQ(0u, heavy_dump_period_ms % min_timer_period_ms);
584  g_heavy_dumps_rate = heavy_dump_period_ms / min_timer_period_ms;
585
586  periodic_dump_timer_.Start(FROM_HERE,
587                             TimeDelta::FromMilliseconds(min_timer_period_ms),
588                             base::Bind(&RequestPeriodicGlobalDump));
589}
590
591void MemoryDumpManager::OnTraceLogDisabled() {
592  subtle::NoBarrier_Store(&memory_tracing_enabled_, 0);
593  scoped_ptr<Thread> dump_thread;
594  {
595    AutoLock lock(lock_);
596    dump_thread = std::move(dump_thread_);
597    session_state_ = nullptr;
598  }
599
600  // Thread stops are blocking and must be performed outside of the |lock_|
601  // or will deadlock (e.g., if ContinueAsyncProcessDump() tries to acquire it).
602  periodic_dump_timer_.Stop();
603  if (dump_thread)
604    dump_thread->Stop();
605}
606
607uint64_t MemoryDumpManager::GetTracingProcessId() const {
608  return delegate_->GetTracingProcessId();
609}
610
611MemoryDumpManager::MemoryDumpProviderInfo::MemoryDumpProviderInfo(
612    MemoryDumpProvider* dump_provider,
613    const char* name,
614    const scoped_refptr<SingleThreadTaskRunner>& task_runner,
615    const MemoryDumpProvider::Options& options)
616    : dump_provider(dump_provider),
617      name(name),
618      task_runner(task_runner),
619      options(options),
620      consecutive_failures(0),
621      disabled(false) {}
622
623MemoryDumpManager::MemoryDumpProviderInfo::~MemoryDumpProviderInfo() {}
624
625bool MemoryDumpManager::MemoryDumpProviderInfo::Comparator::operator()(
626    const scoped_refptr<MemoryDumpManager::MemoryDumpProviderInfo>& a,
627    const scoped_refptr<MemoryDumpManager::MemoryDumpProviderInfo>& b) const {
628  if (!a || !b)
629    return a.get() < b.get();
630  // Ensure that unbound providers (task_runner == nullptr) always run last.
631  // Rationale: some unbound dump providers are known to be slow, keep them last
632  // to avoid skewing timings of the other dump providers.
633  return std::tie(a->task_runner, a->dump_provider) >
634         std::tie(b->task_runner, b->dump_provider);
635}
636
637MemoryDumpManager::ProcessMemoryDumpAsyncState::ProcessMemoryDumpAsyncState(
638    MemoryDumpRequestArgs req_args,
639    const MemoryDumpProviderInfo::OrderedSet& dump_providers,
640    const scoped_refptr<MemoryDumpSessionState>& session_state,
641    MemoryDumpCallback callback,
642    const scoped_refptr<SingleThreadTaskRunner>& dump_thread_task_runner)
643    : req_args(req_args),
644      session_state(session_state),
645      callback(callback),
646      callback_task_runner(MessageLoop::current()->task_runner()),
647      dump_thread_task_runner(dump_thread_task_runner) {
648  pending_dump_providers.reserve(dump_providers.size());
649  pending_dump_providers.assign(dump_providers.rbegin(), dump_providers.rend());
650}
651
652MemoryDumpManager::ProcessMemoryDumpAsyncState::~ProcessMemoryDumpAsyncState() {
653}
654
655ProcessMemoryDump* MemoryDumpManager::ProcessMemoryDumpAsyncState::
656    GetOrCreateMemoryDumpContainerForProcess(ProcessId pid) {
657  auto iter = process_dumps.find(pid);
658  if (iter == process_dumps.end()) {
659    scoped_ptr<ProcessMemoryDump> new_pmd(new ProcessMemoryDump(session_state));
660    iter = process_dumps.insert(std::make_pair(pid, std::move(new_pmd))).first;
661  }
662  return iter->second.get();
663}
664
665}  // namespace trace_event
666}  // namespace base
667