| OLD | NEW |
| (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 "base/trace_event/memory_dump_manager.h" | |
| 6 | |
| 7 #include <algorithm> | |
| 8 | |
| 9 #include "base/atomic_sequence_num.h" | |
| 10 #include "base/compiler_specific.h" | |
| 11 #include "base/hash.h" | |
| 12 #include "base/thread_task_runner_handle.h" | |
| 13 #include "base/trace_event/memory_dump_provider.h" | |
| 14 #include "base/trace_event/memory_dump_session_state.h" | |
| 15 #include "base/trace_event/process_memory_dump.h" | |
| 16 #include "base/trace_event/trace_event_argument.h" | |
| 17 #include "build/build_config.h" | |
| 18 | |
| 19 #if !defined(OS_NACL) | |
| 20 #include "base/trace_event/process_memory_totals_dump_provider.h" | |
| 21 #endif | |
| 22 | |
| 23 #if defined(OS_LINUX) || defined(OS_ANDROID) | |
| 24 #include "base/trace_event/malloc_dump_provider.h" | |
| 25 #include "base/trace_event/process_memory_maps_dump_provider.h" | |
| 26 #endif | |
| 27 | |
| 28 #if defined(OS_ANDROID) | |
| 29 #include "base/trace_event/java_heap_dump_provider_android.h" | |
| 30 #endif | |
| 31 | |
| 32 #if defined(OS_WIN) | |
| 33 #include "base/trace_event/winheap_dump_provider_win.h" | |
| 34 #endif | |
| 35 | |
| 36 namespace base { | |
| 37 namespace trace_event { | |
| 38 | |
| 39 namespace { | |
| 40 | |
| 41 // TODO(primiano): this should be smarter and should do something similar to | |
| 42 // trace event synthetic delays. | |
| 43 const char kTraceCategory[] = TRACE_DISABLED_BY_DEFAULT("memory-infra"); | |
| 44 | |
| 45 // Throttle mmaps at a rate of once every kHeavyMmapsDumpsRate standard dumps. | |
| 46 const int kHeavyMmapsDumpsRate = 8; // 250 ms * 8 = 2000 ms. | |
| 47 const int kDumpIntervalMs = 250; | |
| 48 const int kTraceEventNumArgs = 1; | |
| 49 const char* kTraceEventArgNames[] = {"dumps"}; | |
| 50 const unsigned char kTraceEventArgTypes[] = {TRACE_VALUE_TYPE_CONVERTABLE}; | |
| 51 | |
| 52 StaticAtomicSequenceNumber g_next_guid; | |
| 53 uint32 g_periodic_dumps_count = 0; | |
| 54 MemoryDumpManager* g_instance_for_testing = nullptr; | |
| 55 MemoryDumpProvider* g_mmaps_dump_provider = nullptr; | |
| 56 | |
| 57 void RequestPeriodicGlobalDump() { | |
| 58 MemoryDumpType dump_type = g_periodic_dumps_count == 0 | |
| 59 ? MemoryDumpType::PERIODIC_INTERVAL_WITH_MMAPS | |
| 60 : MemoryDumpType::PERIODIC_INTERVAL; | |
| 61 if (++g_periodic_dumps_count == kHeavyMmapsDumpsRate) | |
| 62 g_periodic_dumps_count = 0; | |
| 63 | |
| 64 MemoryDumpManager::GetInstance()->RequestGlobalDump(dump_type); | |
| 65 } | |
| 66 | |
| 67 } // namespace | |
| 68 | |
| 69 // static | |
| 70 const char* const MemoryDumpManager::kTraceCategoryForTesting = kTraceCategory; | |
| 71 | |
| 72 // static | |
| 73 const uint64 MemoryDumpManager::kInvalidTracingProcessId = 0; | |
| 74 | |
| 75 // static | |
| 76 const int MemoryDumpManager::kMaxConsecutiveFailuresCount = 3; | |
| 77 | |
| 78 // static | |
| 79 MemoryDumpManager* MemoryDumpManager::GetInstance() { | |
| 80 if (g_instance_for_testing) | |
| 81 return g_instance_for_testing; | |
| 82 | |
| 83 return Singleton<MemoryDumpManager, | |
| 84 LeakySingletonTraits<MemoryDumpManager>>::get(); | |
| 85 } | |
| 86 | |
| 87 // static | |
| 88 void MemoryDumpManager::SetInstanceForTesting(MemoryDumpManager* instance) { | |
| 89 if (instance) | |
| 90 instance->skip_core_dumpers_auto_registration_for_testing_ = true; | |
| 91 g_instance_for_testing = instance; | |
| 92 } | |
| 93 | |
| 94 MemoryDumpManager::MemoryDumpManager() | |
| 95 : did_unregister_dump_provider_(false), | |
| 96 delegate_(nullptr), | |
| 97 memory_tracing_enabled_(0), | |
| 98 tracing_process_id_(kInvalidTracingProcessId), | |
| 99 skip_core_dumpers_auto_registration_for_testing_(false) { | |
| 100 g_next_guid.GetNext(); // Make sure that first guid is not zero. | |
| 101 } | |
| 102 | |
| 103 MemoryDumpManager::~MemoryDumpManager() { | |
| 104 base::trace_event::TraceLog::GetInstance()->RemoveEnabledStateObserver(this); | |
| 105 } | |
| 106 | |
| 107 void MemoryDumpManager::Initialize() { | |
| 108 TRACE_EVENT0(kTraceCategory, "init"); // Add to trace-viewer category list. | |
| 109 trace_event::TraceLog::GetInstance()->AddEnabledStateObserver(this); | |
| 110 | |
| 111 if (skip_core_dumpers_auto_registration_for_testing_) | |
| 112 return; | |
| 113 | |
| 114 // Enable the core dump providers. | |
| 115 #if !defined(OS_NACL) | |
| 116 RegisterDumpProvider(ProcessMemoryTotalsDumpProvider::GetInstance()); | |
| 117 #endif | |
| 118 | |
| 119 #if (defined(OS_LINUX) && !defined(FNL_MUSL)) || defined(OS_ANDROID) | |
| 120 g_mmaps_dump_provider = ProcessMemoryMapsDumpProvider::GetInstance(); | |
| 121 RegisterDumpProvider(g_mmaps_dump_provider); | |
| 122 RegisterDumpProvider(MallocDumpProvider::GetInstance()); | |
| 123 #endif | |
| 124 | |
| 125 #if defined(OS_ANDROID) | |
| 126 RegisterDumpProvider(JavaHeapDumpProvider::GetInstance()); | |
| 127 #endif | |
| 128 | |
| 129 #if defined(OS_WIN) | |
| 130 RegisterDumpProvider(WinHeapDumpProvider::GetInstance()); | |
| 131 #endif | |
| 132 } | |
| 133 | |
| 134 void MemoryDumpManager::SetDelegate(MemoryDumpManagerDelegate* delegate) { | |
| 135 AutoLock lock(lock_); | |
| 136 DCHECK_EQ(static_cast<MemoryDumpManagerDelegate*>(nullptr), delegate_); | |
| 137 delegate_ = delegate; | |
| 138 } | |
| 139 | |
| 140 void MemoryDumpManager::RegisterDumpProvider( | |
| 141 MemoryDumpProvider* mdp, | |
| 142 const scoped_refptr<SingleThreadTaskRunner>& task_runner) { | |
| 143 MemoryDumpProviderInfo mdp_info(mdp, task_runner); | |
| 144 AutoLock lock(lock_); | |
| 145 dump_providers_.insert(mdp_info); | |
| 146 } | |
| 147 | |
| 148 void MemoryDumpManager::RegisterDumpProvider(MemoryDumpProvider* mdp) { | |
| 149 RegisterDumpProvider(mdp, nullptr); | |
| 150 } | |
| 151 | |
| 152 void MemoryDumpManager::UnregisterDumpProvider(MemoryDumpProvider* mdp) { | |
| 153 AutoLock lock(lock_); | |
| 154 | |
| 155 auto mdp_iter = dump_providers_.begin(); | |
| 156 for (; mdp_iter != dump_providers_.end(); ++mdp_iter) { | |
| 157 if (mdp_iter->dump_provider == mdp) | |
| 158 break; | |
| 159 } | |
| 160 | |
| 161 if (mdp_iter == dump_providers_.end()) | |
| 162 return; | |
| 163 | |
| 164 // Unregistration of a MemoryDumpProvider while tracing is ongoing is safe | |
| 165 // only if the MDP has specified a thread affinity (via task_runner()) AND | |
| 166 // the unregistration happens on the same thread (so the MDP cannot unregister | |
| 167 // and OnMemoryDump() at the same time). | |
| 168 // Otherwise, it is not possible to guarantee that its unregistration is | |
| 169 // race-free. If you hit this DCHECK, your MDP has a bug. | |
| 170 DCHECK_IMPLIES( | |
| 171 subtle::NoBarrier_Load(&memory_tracing_enabled_), | |
| 172 mdp_iter->task_runner && mdp_iter->task_runner->BelongsToCurrentThread()) | |
| 173 << "The MemoryDumpProvider attempted to unregister itself in a racy way. " | |
| 174 << "Please file a crbug."; | |
| 175 | |
| 176 dump_providers_.erase(mdp_iter); | |
| 177 did_unregister_dump_provider_ = true; | |
| 178 } | |
| 179 | |
| 180 void MemoryDumpManager::RequestGlobalDump( | |
| 181 MemoryDumpType dump_type, | |
| 182 const MemoryDumpCallback& callback) { | |
| 183 // Bail out immediately if tracing is not enabled at all. | |
| 184 if (!UNLIKELY(subtle::NoBarrier_Load(&memory_tracing_enabled_))) | |
| 185 return; | |
| 186 | |
| 187 const uint64 guid = | |
| 188 TraceLog::GetInstance()->MangleEventId(g_next_guid.GetNext()); | |
| 189 | |
| 190 // The delegate_ is supposed to be thread safe, immutable and long lived. | |
| 191 // No need to keep the lock after we ensure that a delegate has been set. | |
| 192 MemoryDumpManagerDelegate* delegate; | |
| 193 { | |
| 194 AutoLock lock(lock_); | |
| 195 delegate = delegate_; | |
| 196 } | |
| 197 | |
| 198 if (delegate) { | |
| 199 // The delegate is in charge to coordinate the request among all the | |
| 200 // processes and call the CreateLocalDumpPoint on the local process. | |
| 201 MemoryDumpRequestArgs args = {guid, dump_type}; | |
| 202 delegate->RequestGlobalMemoryDump(args, callback); | |
| 203 } else if (!callback.is_null()) { | |
| 204 callback.Run(guid, false /* success */); | |
| 205 } | |
| 206 } | |
| 207 | |
| 208 void MemoryDumpManager::RequestGlobalDump(MemoryDumpType dump_type) { | |
| 209 RequestGlobalDump(dump_type, MemoryDumpCallback()); | |
| 210 } | |
| 211 | |
| 212 void MemoryDumpManager::CreateProcessDump(const MemoryDumpRequestArgs& args, | |
| 213 const MemoryDumpCallback& callback) { | |
| 214 scoped_ptr<ProcessMemoryDumpAsyncState> pmd_async_state; | |
| 215 { | |
| 216 AutoLock lock(lock_); | |
| 217 did_unregister_dump_provider_ = false; | |
| 218 pmd_async_state.reset(new ProcessMemoryDumpAsyncState( | |
| 219 args, dump_providers_.begin(), session_state_, callback)); | |
| 220 } | |
| 221 | |
| 222 // Start the thread hop. |dump_providers_| are kept sorted by thread, so | |
| 223 // ContinueAsyncProcessDump will hop at most once per thread (w.r.t. thread | |
| 224 // affinity specified by the MemoryDumpProvider(s) in RegisterDumpProvider()). | |
| 225 ContinueAsyncProcessDump(pmd_async_state.Pass()); | |
| 226 } | |
| 227 | |
| 228 // At most one ContinueAsyncProcessDump() can be active at any time for a given | |
| 229 // PMD, regardless of status of the |lock_|. |lock_| is used here purely to | |
| 230 // ensure consistency w.r.t. (un)registrations of |dump_providers_|. | |
| 231 // The linearization of dump providers' OnMemoryDump invocations is achieved by | |
| 232 // means of subsequent PostTask(s). | |
| 233 // | |
| 234 // 1) Prologue: | |
| 235 // - Check if the dump provider is disabled, if so skip the dump. | |
| 236 // - Check if we are on the right thread. If not hop and continue there. | |
| 237 // 2) Invoke the dump provider's OnMemoryDump() (unless skipped). | |
| 238 // 3) Epilogue: | |
| 239 // - Unregister the dump provider if it failed too many times consecutively. | |
| 240 // - Advance the |next_dump_provider| iterator to the next dump provider. | |
| 241 // - If this was the last hop, create a trace event, add it to the trace | |
| 242 // and finalize (invoke callback). | |
| 243 | |
| 244 void MemoryDumpManager::ContinueAsyncProcessDump( | |
| 245 scoped_ptr<ProcessMemoryDumpAsyncState> pmd_async_state) { | |
| 246 // Initalizes the ThreadLocalEventBuffer to guarantee that the TRACE_EVENTs | |
| 247 // in the PostTask below don't end up registering their own dump providers | |
| 248 // (for discounting trace memory overhead) while holding the |lock_|. | |
| 249 TraceLog::GetInstance()->InitializeThreadLocalEventBufferIfSupported(); | |
| 250 | |
| 251 // DO NOT put any LOG() statement in the locked sections, as in some contexts | |
| 252 // (GPU process) LOG() ends up performing PostTask/IPCs. | |
| 253 MemoryDumpProvider* mdp; | |
| 254 bool skip_dump = false; | |
| 255 { | |
| 256 AutoLock lock(lock_); | |
| 257 // In the unlikely event that a dump provider was unregistered while | |
| 258 // dumping, abort the dump, as that would make |next_dump_provider| invalid. | |
| 259 // Registration, on the other hand, is safe as per std::set<> contract. | |
| 260 if (did_unregister_dump_provider_) { | |
| 261 return AbortDumpLocked(pmd_async_state->callback, | |
| 262 pmd_async_state->task_runner, | |
| 263 pmd_async_state->req_args.dump_guid); | |
| 264 } | |
| 265 | |
| 266 auto* mdp_info = &*pmd_async_state->next_dump_provider; | |
| 267 mdp = mdp_info->dump_provider; | |
| 268 if (mdp_info->disabled) { | |
| 269 skip_dump = true; | |
| 270 } else if (mdp == g_mmaps_dump_provider && | |
| 271 pmd_async_state->req_args.dump_type != | |
| 272 MemoryDumpType::PERIODIC_INTERVAL_WITH_MMAPS) { | |
| 273 // Mmaps dumping is very heavyweight and cannot be performed at the same | |
| 274 // rate of other dumps. TODO(primiano): this is a hack and should be | |
| 275 // cleaned up as part of crbug.com/499731. | |
| 276 skip_dump = true; | |
| 277 } else if (mdp_info->task_runner && | |
| 278 !mdp_info->task_runner->BelongsToCurrentThread()) { | |
| 279 // It's time to hop onto another thread. | |
| 280 | |
| 281 // Copy the callback + arguments just for the unlikley case in which | |
| 282 // PostTask fails. In such case the Bind helper will destroy the | |
| 283 // pmd_async_state and we must keep a copy of the fields to notify the | |
| 284 // abort. | |
| 285 MemoryDumpCallback callback = pmd_async_state->callback; | |
| 286 scoped_refptr<SingleThreadTaskRunner> callback_task_runner = | |
| 287 pmd_async_state->task_runner; | |
| 288 const uint64 dump_guid = pmd_async_state->req_args.dump_guid; | |
| 289 | |
| 290 const bool did_post_task = mdp_info->task_runner->PostTask( | |
| 291 FROM_HERE, Bind(&MemoryDumpManager::ContinueAsyncProcessDump, | |
| 292 Unretained(this), Passed(pmd_async_state.Pass()))); | |
| 293 if (did_post_task) | |
| 294 return; | |
| 295 | |
| 296 // The thread is gone. At this point the best thing we can do is to | |
| 297 // disable the dump provider and abort this dump. | |
| 298 mdp_info->disabled = true; | |
| 299 return AbortDumpLocked(callback, callback_task_runner, dump_guid); | |
| 300 } | |
| 301 } // AutoLock(lock_) | |
| 302 | |
| 303 // Invoke the dump provider without holding the |lock_|. | |
| 304 bool finalize = false; | |
| 305 bool dump_successful = false; | |
| 306 if (!skip_dump) | |
| 307 dump_successful = mdp->OnMemoryDump(&pmd_async_state->process_memory_dump); | |
| 308 | |
| 309 { | |
| 310 AutoLock lock(lock_); | |
| 311 if (did_unregister_dump_provider_) { | |
| 312 return AbortDumpLocked(pmd_async_state->callback, | |
| 313 pmd_async_state->task_runner, | |
| 314 pmd_async_state->req_args.dump_guid); | |
| 315 } | |
| 316 auto* mdp_info = &*pmd_async_state->next_dump_provider; | |
| 317 if (dump_successful) { | |
| 318 mdp_info->consecutive_failures = 0; | |
| 319 } else if (!skip_dump) { | |
| 320 ++mdp_info->consecutive_failures; | |
| 321 if (mdp_info->consecutive_failures >= kMaxConsecutiveFailuresCount) { | |
| 322 mdp_info->disabled = true; | |
| 323 } | |
| 324 } | |
| 325 ++pmd_async_state->next_dump_provider; | |
| 326 finalize = pmd_async_state->next_dump_provider == dump_providers_.end(); | |
| 327 } | |
| 328 | |
| 329 if (!skip_dump && !dump_successful) { | |
| 330 LOG(ERROR) << "A memory dumper failed, possibly due to sandboxing " | |
| 331 "(crbug.com/461788). Disabling dumper for current process. " | |
| 332 "Try restarting chrome with the --no-sandbox switch."; | |
| 333 } | |
| 334 | |
| 335 if (finalize) | |
| 336 return FinalizeDumpAndAddToTrace(pmd_async_state.Pass()); | |
| 337 | |
| 338 ContinueAsyncProcessDump(pmd_async_state.Pass()); | |
| 339 } | |
| 340 | |
| 341 // static | |
| 342 void MemoryDumpManager::FinalizeDumpAndAddToTrace( | |
| 343 scoped_ptr<ProcessMemoryDumpAsyncState> pmd_async_state) { | |
| 344 if (!pmd_async_state->task_runner->BelongsToCurrentThread()) { | |
| 345 scoped_refptr<SingleThreadTaskRunner> task_runner = | |
| 346 pmd_async_state->task_runner; | |
| 347 task_runner->PostTask(FROM_HERE, | |
| 348 Bind(&MemoryDumpManager::FinalizeDumpAndAddToTrace, | |
| 349 Passed(pmd_async_state.Pass()))); | |
| 350 return; | |
| 351 } | |
| 352 | |
| 353 scoped_refptr<ConvertableToTraceFormat> event_value(new TracedValue()); | |
| 354 pmd_async_state->process_memory_dump.AsValueInto( | |
| 355 static_cast<TracedValue*>(event_value.get())); | |
| 356 const char* const event_name = | |
| 357 MemoryDumpTypeToString(pmd_async_state->req_args.dump_type); | |
| 358 | |
| 359 TRACE_EVENT_API_ADD_TRACE_EVENT( | |
| 360 TRACE_EVENT_PHASE_MEMORY_DUMP, | |
| 361 TraceLog::GetCategoryGroupEnabled(kTraceCategory), event_name, | |
| 362 pmd_async_state->req_args.dump_guid, kTraceEventNumArgs, | |
| 363 kTraceEventArgNames, kTraceEventArgTypes, nullptr /* arg_values */, | |
| 364 &event_value, TRACE_EVENT_FLAG_HAS_ID); | |
| 365 | |
| 366 if (!pmd_async_state->callback.is_null()) { | |
| 367 pmd_async_state->callback.Run(pmd_async_state->req_args.dump_guid, | |
| 368 true /* success */); | |
| 369 pmd_async_state->callback.Reset(); | |
| 370 } | |
| 371 } | |
| 372 | |
| 373 // static | |
| 374 void MemoryDumpManager::AbortDumpLocked( | |
| 375 MemoryDumpCallback callback, | |
| 376 scoped_refptr<SingleThreadTaskRunner> task_runner, | |
| 377 uint64 dump_guid) { | |
| 378 if (callback.is_null()) | |
| 379 return; // There is nothing to NACK. | |
| 380 | |
| 381 // Post the callback even if we are already on the right thread to avoid | |
| 382 // invoking the callback while holding the lock_. | |
| 383 task_runner->PostTask(FROM_HERE, | |
| 384 Bind(callback, dump_guid, false /* success */)); | |
| 385 } | |
| 386 | |
| 387 void MemoryDumpManager::OnTraceLogEnabled() { | |
| 388 // TODO(primiano): at this point we query TraceLog::GetCurrentCategoryFilter | |
| 389 // to figure out (and cache) which dumpers should be enabled or not. | |
| 390 // For the moment piggy back everything on the generic "memory" category. | |
| 391 bool enabled; | |
| 392 TRACE_EVENT_CATEGORY_GROUP_ENABLED(kTraceCategory, &enabled); | |
| 393 | |
| 394 // Initialize the TraceLog for the current thread. This is to avoid that the | |
| 395 // TraceLog memory dump provider is registered lazily in the PostTask() below | |
| 396 // while the |lock_| is taken; | |
| 397 TraceLog::GetInstance()->InitializeThreadLocalEventBufferIfSupported(); | |
| 398 | |
| 399 AutoLock lock(lock_); | |
| 400 | |
| 401 // There is no point starting the tracing without a delegate. | |
| 402 if (!enabled || !delegate_) { | |
| 403 // Disable all the providers. | |
| 404 for (auto it = dump_providers_.begin(); it != dump_providers_.end(); ++it) | |
| 405 it->disabled = true; | |
| 406 return; | |
| 407 } | |
| 408 | |
| 409 session_state_ = new MemoryDumpSessionState(); | |
| 410 for (auto it = dump_providers_.begin(); it != dump_providers_.end(); ++it) { | |
| 411 it->disabled = false; | |
| 412 it->consecutive_failures = 0; | |
| 413 } | |
| 414 | |
| 415 subtle::NoBarrier_Store(&memory_tracing_enabled_, 1); | |
| 416 | |
| 417 if (delegate_->IsCoordinatorProcess()) { | |
| 418 g_periodic_dumps_count = 0; | |
| 419 periodic_dump_timer_.Start(FROM_HERE, | |
| 420 TimeDelta::FromMilliseconds(kDumpIntervalMs), | |
| 421 base::Bind(&RequestPeriodicGlobalDump)); | |
| 422 } | |
| 423 } | |
| 424 | |
| 425 void MemoryDumpManager::OnTraceLogDisabled() { | |
| 426 AutoLock lock(lock_); | |
| 427 periodic_dump_timer_.Stop(); | |
| 428 subtle::NoBarrier_Store(&memory_tracing_enabled_, 0); | |
| 429 session_state_ = nullptr; | |
| 430 } | |
| 431 | |
| 432 // static | |
| 433 uint64 MemoryDumpManager::ChildProcessIdToTracingProcessId( | |
| 434 int child_process_id) { | |
| 435 return static_cast<uint64>( | |
| 436 Hash(reinterpret_cast<const char*>(&child_process_id), | |
| 437 sizeof(child_process_id))) + | |
| 438 1; | |
| 439 } | |
| 440 | |
| 441 MemoryDumpManager::MemoryDumpProviderInfo::MemoryDumpProviderInfo( | |
| 442 MemoryDumpProvider* dump_provider, | |
| 443 const scoped_refptr<SingleThreadTaskRunner>& task_runner) | |
| 444 : dump_provider(dump_provider), | |
| 445 task_runner(task_runner), | |
| 446 consecutive_failures(0), | |
| 447 disabled(false) {} | |
| 448 | |
| 449 MemoryDumpManager::MemoryDumpProviderInfo::~MemoryDumpProviderInfo() { | |
| 450 } | |
| 451 | |
| 452 bool MemoryDumpManager::MemoryDumpProviderInfo::operator<( | |
| 453 const MemoryDumpProviderInfo& other) const { | |
| 454 if (task_runner == other.task_runner) | |
| 455 return dump_provider < other.dump_provider; | |
| 456 return task_runner < other.task_runner; | |
| 457 } | |
| 458 | |
| 459 MemoryDumpManager::ProcessMemoryDumpAsyncState::ProcessMemoryDumpAsyncState( | |
| 460 MemoryDumpRequestArgs req_args, | |
| 461 MemoryDumpProviderInfoSet::iterator next_dump_provider, | |
| 462 const scoped_refptr<MemoryDumpSessionState>& session_state, | |
| 463 MemoryDumpCallback callback) | |
| 464 : process_memory_dump(session_state), | |
| 465 req_args(req_args), | |
| 466 next_dump_provider(next_dump_provider), | |
| 467 callback(callback), | |
| 468 task_runner(MessageLoop::current()->task_runner()) {} | |
| 469 | |
| 470 MemoryDumpManager::ProcessMemoryDumpAsyncState::~ProcessMemoryDumpAsyncState() { | |
| 471 } | |
| 472 | |
| 473 } // namespace trace_event | |
| 474 } // namespace base | |
| OLD | NEW |