OLD | NEW |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "base/trace_event/memory_dump_manager.h" | 5 #include "base/trace_event/memory_dump_manager.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 | 8 |
9 #include "base/atomic_sequence_num.h" | 9 #include "base/atomic_sequence_num.h" |
10 #include "base/compiler_specific.h" | 10 #include "base/compiler_specific.h" |
11 #include "base/trace_event/memory_dump_provider.h" | 11 #include "base/trace_event/memory_dump_provider.h" |
12 #include "base/trace_event/memory_dump_session_state.h" | 12 #include "base/trace_event/memory_dump_session_state.h" |
13 #include "base/trace_event/process_memory_dump.h" | 13 #include "base/trace_event/process_memory_dump.h" |
14 #include "base/trace_event/trace_event_argument.h" | 14 #include "base/trace_event/trace_event_argument.h" |
| 15 #include "build/build_config.h" |
| 16 |
| 17 #if !defined(OS_NACL) |
| 18 #include "base/trace_event/process_memory_totals_dump_provider.h" |
| 19 #endif |
15 | 20 |
16 #if defined(OS_LINUX) || defined(OS_ANDROID) | 21 #if defined(OS_LINUX) || defined(OS_ANDROID) |
17 #include "base/trace_event/malloc_dump_provider.h" | 22 #include "base/trace_event/malloc_dump_provider.h" |
18 #include "base/trace_event/process_memory_maps_dump_provider.h" | 23 #include "base/trace_event/process_memory_maps_dump_provider.h" |
19 #include "base/trace_event/process_memory_totals_dump_provider.h" | 24 #endif |
20 #elif defined(OS_WIN) | 25 |
| 26 #if defined(OS_ANDROID) |
| 27 #include "base/trace_event/java_heap_dump_provider_android.h" |
| 28 #endif |
| 29 |
| 30 #if defined(OS_WIN) |
21 #include "base/trace_event/winheap_dump_provider_win.h" | 31 #include "base/trace_event/winheap_dump_provider_win.h" |
22 #endif | 32 #endif |
23 | 33 |
24 namespace base { | 34 namespace base { |
25 namespace trace_event { | 35 namespace trace_event { |
26 | 36 |
27 namespace { | 37 namespace { |
28 | 38 |
29 // TODO(primiano): this should be smarter and should do something similar to | 39 // TODO(primiano): this should be smarter and should do something similar to |
30 // trace event synthetic delays. | 40 // trace event synthetic delays. |
(...skipping 106 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
137 } | 147 } |
138 | 148 |
139 // static | 149 // static |
140 void MemoryDumpManager::SetInstanceForTesting(MemoryDumpManager* instance) { | 150 void MemoryDumpManager::SetInstanceForTesting(MemoryDumpManager* instance) { |
141 if (instance) | 151 if (instance) |
142 instance->skip_core_dumpers_auto_registration_for_testing_ = true; | 152 instance->skip_core_dumpers_auto_registration_for_testing_ = true; |
143 g_instance_for_testing = instance; | 153 g_instance_for_testing = instance; |
144 } | 154 } |
145 | 155 |
146 MemoryDumpManager::MemoryDumpManager() | 156 MemoryDumpManager::MemoryDumpManager() |
147 : dump_provider_currently_active_(nullptr), | 157 : delegate_(nullptr), |
148 delegate_(nullptr), | |
149 memory_tracing_enabled_(0), | 158 memory_tracing_enabled_(0), |
150 skip_core_dumpers_auto_registration_for_testing_(false) { | 159 skip_core_dumpers_auto_registration_for_testing_(false) { |
151 g_next_guid.GetNext(); // Make sure that first guid is not zero. | 160 g_next_guid.GetNext(); // Make sure that first guid is not zero. |
152 } | 161 } |
153 | 162 |
154 MemoryDumpManager::~MemoryDumpManager() { | 163 MemoryDumpManager::~MemoryDumpManager() { |
155 base::trace_event::TraceLog::GetInstance()->RemoveEnabledStateObserver(this); | 164 base::trace_event::TraceLog::GetInstance()->RemoveEnabledStateObserver(this); |
156 } | 165 } |
157 | 166 |
158 void MemoryDumpManager::Initialize() { | 167 void MemoryDumpManager::Initialize() { |
159 TRACE_EVENT0(kTraceCategory, "init"); // Add to trace-viewer category list. | 168 TRACE_EVENT0(kTraceCategory, "init"); // Add to trace-viewer category list. |
160 trace_event::TraceLog::GetInstance()->AddEnabledStateObserver(this); | 169 trace_event::TraceLog::GetInstance()->AddEnabledStateObserver(this); |
161 | 170 |
162 if (skip_core_dumpers_auto_registration_for_testing_) | 171 if (skip_core_dumpers_auto_registration_for_testing_) |
163 return; | 172 return; |
164 | 173 |
| 174 // Enable the core dump providers. |
| 175 #if !defined(OS_NACL) |
| 176 RegisterDumpProvider(ProcessMemoryTotalsDumpProvider::GetInstance()); |
| 177 #endif |
| 178 |
165 #if defined(OS_LINUX) || defined(OS_ANDROID) | 179 #if defined(OS_LINUX) || defined(OS_ANDROID) |
166 // Enable the core dump providers. | |
167 RegisterDumpProvider(ProcessMemoryTotalsDumpProvider::GetInstance()); | |
168 RegisterDumpProvider(ProcessMemoryMapsDumpProvider::GetInstance()); | 180 RegisterDumpProvider(ProcessMemoryMapsDumpProvider::GetInstance()); |
169 RegisterDumpProvider(MallocDumpProvider::GetInstance()); | 181 RegisterDumpProvider(MallocDumpProvider::GetInstance()); |
170 #elif defined(OS_WIN) | 182 #endif |
| 183 |
| 184 #if defined(OS_ANDROID) |
| 185 RegisterDumpProvider(JavaHeapDumpProvider::GetInstance()); |
| 186 #endif |
| 187 |
| 188 #if defined(OS_WIN) |
171 RegisterDumpProvider(WinHeapDumpProvider::GetInstance()); | 189 RegisterDumpProvider(WinHeapDumpProvider::GetInstance()); |
172 #endif | 190 #endif |
173 } | 191 } |
174 | 192 |
175 void MemoryDumpManager::SetDelegate(MemoryDumpManagerDelegate* delegate) { | 193 void MemoryDumpManager::SetDelegate(MemoryDumpManagerDelegate* delegate) { |
176 AutoLock lock(lock_); | 194 AutoLock lock(lock_); |
177 DCHECK_EQ(static_cast<MemoryDumpManagerDelegate*>(nullptr), delegate_); | 195 DCHECK_EQ(static_cast<MemoryDumpManagerDelegate*>(nullptr), delegate_); |
178 delegate_ = delegate; | 196 delegate_ = delegate; |
179 } | 197 } |
180 | 198 |
| 199 void MemoryDumpManager::RegisterDumpProvider( |
| 200 MemoryDumpProvider* mdp, |
| 201 const scoped_refptr<SingleThreadTaskRunner>& task_runner) { |
| 202 MemoryDumpProviderInfo mdp_info(task_runner); |
| 203 AutoLock lock(lock_); |
| 204 dump_providers_.insert(std::make_pair(mdp, mdp_info)); |
| 205 } |
| 206 |
181 void MemoryDumpManager::RegisterDumpProvider(MemoryDumpProvider* mdp) { | 207 void MemoryDumpManager::RegisterDumpProvider(MemoryDumpProvider* mdp) { |
182 AutoLock lock(lock_); | 208 RegisterDumpProvider(mdp, nullptr); |
183 dump_providers_registered_.insert(mdp); | |
184 } | 209 } |
185 | 210 |
186 void MemoryDumpManager::UnregisterDumpProvider(MemoryDumpProvider* mdp) { | 211 void MemoryDumpManager::UnregisterDumpProvider(MemoryDumpProvider* mdp) { |
187 AutoLock lock(lock_); | 212 AutoLock lock(lock_); |
188 | 213 |
| 214 auto it = dump_providers_.find(mdp); |
| 215 if (it == dump_providers_.end()) |
| 216 return; |
| 217 |
| 218 const MemoryDumpProviderInfo& mdp_info = it->second; |
189 // Unregistration of a MemoryDumpProvider while tracing is ongoing is safe | 219 // Unregistration of a MemoryDumpProvider while tracing is ongoing is safe |
190 // only if the MDP has specified a thread affinity (via task_runner()) AND | 220 // only if the MDP has specified a thread affinity (via task_runner()) AND |
191 // the unregistration happens on the same thread (so the MDP cannot unregister | 221 // the unregistration happens on the same thread (so the MDP cannot unregister |
192 // and DumpInto() at the same time). | 222 // and OnMemoryDump() at the same time). |
193 // Otherwise, it is not possible to guarantee that its unregistration is | 223 // Otherwise, it is not possible to guarantee that its unregistration is |
194 // race-free. If you hit this DCHECK, your MDP has a bug. | 224 // race-free. If you hit this DCHECK, your MDP has a bug. |
195 DCHECK_IMPLIES( | 225 DCHECK_IMPLIES( |
196 subtle::NoBarrier_Load(&memory_tracing_enabled_), | 226 subtle::NoBarrier_Load(&memory_tracing_enabled_), |
197 mdp->task_runner() && mdp->task_runner()->BelongsToCurrentThread()) | 227 mdp_info.task_runner && mdp_info.task_runner->BelongsToCurrentThread()) |
198 << "The MemoryDumpProvider " << mdp->GetFriendlyName() << " attempted to " | 228 << "The MemoryDumpProvider attempted to unregister itself in a racy way. " |
199 << "unregister itself in a racy way. Please file a crbug."; | 229 << " Please file a crbug."; |
200 | 230 |
201 // Remove from the enabled providers list. This is to deal with the case that | 231 // Remove from the enabled providers list. This is to deal with the case that |
202 // UnregisterDumpProvider is called while the trace is enabled. | 232 // UnregisterDumpProvider is called while the trace is enabled. |
203 dump_providers_enabled_.erase(mdp); | 233 dump_providers_.erase(it); |
204 dump_providers_registered_.erase(mdp); | |
205 } | 234 } |
206 | 235 |
207 void MemoryDumpManager::RequestGlobalDump( | 236 void MemoryDumpManager::RequestGlobalDump( |
208 MemoryDumpType dump_type, | 237 MemoryDumpType dump_type, |
209 const MemoryDumpCallback& callback) { | 238 const MemoryDumpCallback& callback) { |
210 // Bail out immediately if tracing is not enabled at all. | 239 // Bail out immediately if tracing is not enabled at all. |
211 if (!UNLIKELY(subtle::NoBarrier_Load(&memory_tracing_enabled_))) | 240 if (!UNLIKELY(subtle::NoBarrier_Load(&memory_tracing_enabled_))) |
212 return; | 241 return; |
213 | 242 |
214 const uint64 guid = | 243 const uint64 guid = |
(...skipping 22 matching lines...) Expand all Loading... |
237 } | 266 } |
238 | 267 |
239 // Creates a memory dump for the current process and appends it to the trace. | 268 // Creates a memory dump for the current process and appends it to the trace. |
240 void MemoryDumpManager::CreateProcessDump(const MemoryDumpRequestArgs& args, | 269 void MemoryDumpManager::CreateProcessDump(const MemoryDumpRequestArgs& args, |
241 const MemoryDumpCallback& callback) { | 270 const MemoryDumpCallback& callback) { |
242 scoped_refptr<ProcessMemoryDumpHolder> pmd_holder( | 271 scoped_refptr<ProcessMemoryDumpHolder> pmd_holder( |
243 new ProcessMemoryDumpHolder(args, session_state_, callback)); | 272 new ProcessMemoryDumpHolder(args, session_state_, callback)); |
244 ProcessMemoryDump* pmd = &pmd_holder->process_memory_dump; | 273 ProcessMemoryDump* pmd = &pmd_holder->process_memory_dump; |
245 bool did_any_provider_dump = false; | 274 bool did_any_provider_dump = false; |
246 | 275 |
247 // Iterate over the active dump providers and invoke DumpInto(pmd). | 276 // Iterate over the active dump providers and invoke OnMemoryDump(pmd). |
248 // The MDM guarantees linearity (at most one MDP is active within one | 277 // The MDM guarantees linearity (at most one MDP is active within one |
249 // process) and thread-safety (MDM enforces the right locking when entering / | 278 // process) and thread-safety (MDM enforces the right locking when entering / |
250 // leaving the MDP.DumpInto() call). This is to simplify the clients' design | 279 // leaving the MDP.OnMemoryDump() call). This is to simplify the clients' |
| 280 // design |
251 // and not let the MDPs worry about locking. | 281 // and not let the MDPs worry about locking. |
252 // As regards thread affinity, depending on the MDP configuration (see | 282 // As regards thread affinity, depending on the MDP configuration (see |
253 // memory_dump_provider.h), the DumpInto() invocation can happen: | 283 // memory_dump_provider.h), the OnMemoryDump() invocation can happen: |
254 // - Synchronousy on the MDM thread, when MDP.task_runner() is not set. | 284 // - Synchronousy on the MDM thread, when MDP.task_runner() is not set. |
255 // - Posted on MDP.task_runner(), when MDP.task_runner() is set. | 285 // - Posted on MDP.task_runner(), when MDP.task_runner() is set. |
256 { | 286 { |
257 AutoLock lock(lock_); | 287 AutoLock lock(lock_); |
258 for (auto dump_provider_iter = dump_providers_enabled_.begin(); | 288 for (auto it = dump_providers_.begin(); it != dump_providers_.end(); ++it) { |
259 dump_provider_iter != dump_providers_enabled_.end();) { | 289 MemoryDumpProvider* mdp = it->first; |
260 // InvokeDumpProviderLocked will remove the MDP from the set if it fails. | 290 MemoryDumpProviderInfo* mdp_info = &it->second; |
261 MemoryDumpProvider* mdp = *dump_provider_iter; | 291 if (mdp_info->disabled) |
262 ++dump_provider_iter; | 292 continue; |
263 if (mdp->task_runner()) { | 293 if (mdp_info->task_runner) { |
264 // The DumpInto() call must be posted. | 294 // The OnMemoryDump() call must be posted. |
265 bool did_post_async_task = mdp->task_runner()->PostTask( | 295 bool did_post_async_task = mdp_info->task_runner->PostTask( |
266 FROM_HERE, Bind(&MemoryDumpManager::ContinueAsyncProcessDump, | 296 FROM_HERE, Bind(&MemoryDumpManager::ContinueAsyncProcessDump, |
267 Unretained(this), Unretained(mdp), pmd_holder)); | 297 Unretained(this), Unretained(mdp), pmd_holder)); |
268 // The thread underlying the TaskRunner might have gone away. | 298 // The thread underlying the TaskRunner might have gone away. |
269 if (did_post_async_task) | 299 if (did_post_async_task) |
270 ++pmd_holder->num_pending_async_requests; | 300 ++pmd_holder->num_pending_async_requests; |
271 } else { | 301 } else { |
272 // Invoke the dump provider synchronously. | 302 // Invoke the dump provider synchronously. |
273 did_any_provider_dump |= InvokeDumpProviderLocked(mdp, pmd); | 303 did_any_provider_dump |= InvokeDumpProviderLocked(mdp, pmd); |
274 } | 304 } |
275 } | 305 } |
276 } // AutoLock | 306 } // AutoLock |
277 | 307 |
278 // If at least one synchronous provider did dump and there are no pending | 308 // If at least one synchronous provider did dump and there are no pending |
279 // asynchronous requests, add the dump to the trace and invoke the callback | 309 // asynchronous requests, add the dump to the trace and invoke the callback |
280 // straight away (FinalizeDumpAndAddToTrace() takes care of the callback). | 310 // straight away (FinalizeDumpAndAddToTrace() takes care of the callback). |
281 if (did_any_provider_dump && pmd_holder->num_pending_async_requests == 0) | 311 if (did_any_provider_dump && pmd_holder->num_pending_async_requests == 0) |
282 FinalizeDumpAndAddToTrace(pmd_holder); | 312 FinalizeDumpAndAddToTrace(pmd_holder); |
283 } | 313 } |
284 | 314 |
285 // Invokes the MemoryDumpProvider.DumpInto(), taking care of the failsafe logic | 315 // Invokes the MemoryDumpProvider.OnMemoryDump(), taking care of the fail-safe |
286 // which disables the dumper when failing (crbug.com/461788). | 316 // logic which disables the dumper when failing (crbug.com/461788). |
287 bool MemoryDumpManager::InvokeDumpProviderLocked(MemoryDumpProvider* mdp, | 317 bool MemoryDumpManager::InvokeDumpProviderLocked(MemoryDumpProvider* mdp, |
288 ProcessMemoryDump* pmd) { | 318 ProcessMemoryDump* pmd) { |
289 lock_.AssertAcquired(); | 319 lock_.AssertAcquired(); |
290 dump_provider_currently_active_ = mdp; | 320 bool dump_successful = mdp->OnMemoryDump(pmd); |
291 bool dump_successful = mdp->DumpInto(pmd); | |
292 dump_provider_currently_active_ = nullptr; | |
293 if (!dump_successful) { | 321 if (!dump_successful) { |
294 LOG(ERROR) << "The memory dumper " << mdp->GetFriendlyName() | 322 LOG(ERROR) << "The memory dumper failed, possibly due to sandboxing " |
295 << " failed, possibly due to sandboxing (crbug.com/461788), " | 323 "(crbug.com/461788), disabling it for current process. Try " |
296 "disabling it for current process. Try restarting chrome " | 324 "restarting chrome with the --no-sandbox switch."; |
297 "with the --no-sandbox switch."; | 325 dump_providers_.find(mdp)->second.disabled = true; |
298 dump_providers_enabled_.erase(mdp); | |
299 } | 326 } |
300 return dump_successful; | 327 return dump_successful; |
301 } | 328 } |
302 | 329 |
303 // This is posted to arbitrary threads as a continuation of CreateProcessDump(), | 330 // This is posted to arbitrary threads as a continuation of CreateProcessDump(), |
304 // when one or more MemoryDumpProvider(s) require the DumpInto() call to happen | 331 // when one or more MemoryDumpProvider(s) require the OnMemoryDump() call to |
305 // on a different thread. | 332 // happen on a different thread. |
306 void MemoryDumpManager::ContinueAsyncProcessDump( | 333 void MemoryDumpManager::ContinueAsyncProcessDump( |
307 MemoryDumpProvider* mdp, | 334 MemoryDumpProvider* mdp, |
308 scoped_refptr<ProcessMemoryDumpHolder> pmd_holder) { | 335 scoped_refptr<ProcessMemoryDumpHolder> pmd_holder) { |
309 bool should_finalize_dump = false; | 336 bool should_finalize_dump = false; |
310 { | 337 { |
311 // The lock here is to guarantee that different asynchronous dumps on | 338 // The lock here is to guarantee that different asynchronous dumps on |
312 // different threads are still serialized, so that the MemoryDumpProvider | 339 // different threads are still serialized, so that the MemoryDumpProvider |
313 // has a consistent view of the |pmd| argument passed. | 340 // has a consistent view of the |pmd| argument passed. |
314 AutoLock lock(lock_); | 341 AutoLock lock(lock_); |
315 ProcessMemoryDump* pmd = &pmd_holder->process_memory_dump; | 342 ProcessMemoryDump* pmd = &pmd_holder->process_memory_dump; |
316 | 343 |
317 // Check if the MemoryDumpProvider is still there. It might have been | 344 // Check if the MemoryDumpProvider is still there. It might have been |
318 // destroyed and unregistered while hopping threads. | 345 // destroyed and unregistered while hopping threads. |
319 if (dump_providers_enabled_.count(mdp)) | 346 if (dump_providers_.count(mdp)) |
320 InvokeDumpProviderLocked(mdp, pmd); | 347 InvokeDumpProviderLocked(mdp, pmd); |
321 | 348 |
322 // Finalize the dump appending it to the trace if this was the last | 349 // Finalize the dump appending it to the trace if this was the last |
323 // asynchronous request pending. | 350 // asynchronous request pending. |
324 --pmd_holder->num_pending_async_requests; | 351 --pmd_holder->num_pending_async_requests; |
325 if (pmd_holder->num_pending_async_requests == 0) | 352 if (pmd_holder->num_pending_async_requests == 0) |
326 should_finalize_dump = true; | 353 should_finalize_dump = true; |
327 } // AutoLock(lock_) | 354 } // AutoLock(lock_) |
328 | 355 |
329 if (should_finalize_dump) | 356 if (should_finalize_dump) |
330 FinalizeDumpAndAddToTrace(pmd_holder); | 357 FinalizeDumpAndAddToTrace(pmd_holder); |
331 } | 358 } |
332 | 359 |
333 void MemoryDumpManager::OnTraceLogEnabled() { | 360 void MemoryDumpManager::OnTraceLogEnabled() { |
334 // TODO(primiano): at this point we query TraceLog::GetCurrentCategoryFilter | 361 // TODO(primiano): at this point we query TraceLog::GetCurrentCategoryFilter |
335 // to figure out (and cache) which dumpers should be enabled or not. | 362 // to figure out (and cache) which dumpers should be enabled or not. |
336 // For the moment piggy back everything on the generic "memory" category. | 363 // For the moment piggy back everything on the generic "memory" category. |
337 bool enabled; | 364 bool enabled; |
338 TRACE_EVENT_CATEGORY_GROUP_ENABLED(kTraceCategory, &enabled); | 365 TRACE_EVENT_CATEGORY_GROUP_ENABLED(kTraceCategory, &enabled); |
339 | 366 |
340 AutoLock lock(lock_); | 367 AutoLock lock(lock_); |
341 | 368 |
342 // There is no point starting the tracing without a delegate. | 369 // There is no point starting the tracing without a delegate. |
343 if (!enabled || !delegate_) { | 370 if (!enabled || !delegate_) { |
344 dump_providers_enabled_.clear(); | 371 // Disable all the providers. |
| 372 for (auto it = dump_providers_.begin(); it != dump_providers_.end(); ++it) |
| 373 it->second.disabled = true; |
345 return; | 374 return; |
346 } | 375 } |
347 | 376 |
348 // Merge the dictionary of allocator attributes from all dump providers | |
349 // into the session state. | |
350 session_state_ = new MemoryDumpSessionState(); | 377 session_state_ = new MemoryDumpSessionState(); |
351 for (const MemoryDumpProvider* mdp : dump_providers_registered_) { | 378 for (auto it = dump_providers_.begin(); it != dump_providers_.end(); ++it) |
352 session_state_->allocators_attributes_type_info.Update( | 379 it->second.disabled = false; |
353 mdp->allocator_attributes_type_info()); | 380 |
354 } | |
355 dump_providers_enabled_ = dump_providers_registered_; | |
356 subtle::NoBarrier_Store(&memory_tracing_enabled_, 1); | 381 subtle::NoBarrier_Store(&memory_tracing_enabled_, 1); |
357 | 382 |
358 if (delegate_->IsCoordinatorProcess()) { | 383 if (delegate_->IsCoordinatorProcess()) { |
359 periodic_dump_timer_.Start(FROM_HERE, | 384 periodic_dump_timer_.Start(FROM_HERE, |
360 TimeDelta::FromSeconds(kDumpIntervalSeconds), | 385 TimeDelta::FromSeconds(kDumpIntervalSeconds), |
361 base::Bind(&RequestPeriodicGlobalDump)); | 386 base::Bind(&RequestPeriodicGlobalDump)); |
362 } | 387 } |
363 } | 388 } |
364 | 389 |
365 void MemoryDumpManager::OnTraceLogDisabled() { | 390 void MemoryDumpManager::OnTraceLogDisabled() { |
366 AutoLock lock(lock_); | 391 AutoLock lock(lock_); |
367 periodic_dump_timer_.Stop(); | 392 periodic_dump_timer_.Stop(); |
368 dump_providers_enabled_.clear(); | |
369 subtle::NoBarrier_Store(&memory_tracing_enabled_, 0); | 393 subtle::NoBarrier_Store(&memory_tracing_enabled_, 0); |
370 session_state_ = nullptr; | 394 session_state_ = nullptr; |
371 } | 395 } |
372 | 396 |
| 397 MemoryDumpManager::MemoryDumpProviderInfo::MemoryDumpProviderInfo( |
| 398 const scoped_refptr<SingleThreadTaskRunner>& task_runner) |
| 399 : task_runner(task_runner), disabled(false) { |
| 400 } |
| 401 MemoryDumpManager::MemoryDumpProviderInfo::~MemoryDumpProviderInfo() { |
| 402 } |
| 403 |
373 } // namespace trace_event | 404 } // namespace trace_event |
374 } // namespace base | 405 } // namespace base |
OLD | NEW |