| 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 <inttypes.h> | 7 #include <inttypes.h> | 
| 8 #include <stdio.h> | 8 #include <stdio.h> | 
| 9 | 9 | 
| 10 #include <algorithm> | 10 #include <algorithm> | 
| (...skipping 18 matching lines...) Expand all  Loading... | 
| 29 #include "base/trace_event/heap_profiler_allocation_context_tracker.h" | 29 #include "base/trace_event/heap_profiler_allocation_context_tracker.h" | 
| 30 #include "base/trace_event/heap_profiler_event_filter.h" | 30 #include "base/trace_event/heap_profiler_event_filter.h" | 
| 31 #include "base/trace_event/heap_profiler_stack_frame_deduplicator.h" | 31 #include "base/trace_event/heap_profiler_stack_frame_deduplicator.h" | 
| 32 #include "base/trace_event/heap_profiler_type_name_deduplicator.h" | 32 #include "base/trace_event/heap_profiler_type_name_deduplicator.h" | 
| 33 #include "base/trace_event/malloc_dump_provider.h" | 33 #include "base/trace_event/malloc_dump_provider.h" | 
| 34 #include "base/trace_event/memory_dump_provider.h" | 34 #include "base/trace_event/memory_dump_provider.h" | 
| 35 #include "base/trace_event/memory_dump_scheduler.h" | 35 #include "base/trace_event/memory_dump_scheduler.h" | 
| 36 #include "base/trace_event/memory_dump_session_state.h" | 36 #include "base/trace_event/memory_dump_session_state.h" | 
| 37 #include "base/trace_event/memory_infra_background_whitelist.h" | 37 #include "base/trace_event/memory_infra_background_whitelist.h" | 
| 38 #include "base/trace_event/memory_peak_detector.h" | 38 #include "base/trace_event/memory_peak_detector.h" | 
|  | 39 #include "base/trace_event/memory_tracing_observer.h" | 
| 39 #include "base/trace_event/process_memory_dump.h" | 40 #include "base/trace_event/process_memory_dump.h" | 
| 40 #include "base/trace_event/trace_event.h" | 41 #include "base/trace_event/trace_event.h" | 
| 41 #include "base/trace_event/trace_event_argument.h" | 42 #include "base/trace_event/trace_event_argument.h" | 
| 42 #include "build/build_config.h" | 43 #include "build/build_config.h" | 
| 43 | 44 | 
| 44 #if defined(OS_ANDROID) | 45 #if defined(OS_ANDROID) | 
| 45 #include "base/trace_event/java_heap_dump_provider_android.h" | 46 #include "base/trace_event/java_heap_dump_provider_android.h" | 
| 46 #endif | 47 #endif | 
| 47 | 48 | 
| 48 namespace base { | 49 namespace base { | 
| 49 namespace trace_event { | 50 namespace trace_event { | 
| 50 | 51 | 
| 51 namespace { | 52 namespace { | 
| 52 | 53 | 
| 53 const int kTraceEventNumArgs = 1; |  | 
| 54 const char* kTraceEventArgNames[] = {"dumps"}; |  | 
| 55 const unsigned char kTraceEventArgTypes[] = {TRACE_VALUE_TYPE_CONVERTABLE}; |  | 
| 56 |  | 
| 57 StaticAtomicSequenceNumber g_next_guid; | 54 StaticAtomicSequenceNumber g_next_guid; | 
| 58 MemoryDumpManager* g_instance_for_testing = nullptr; | 55 MemoryDumpManager* g_instance_for_testing = nullptr; | 
| 59 | 56 | 
| 60 // The list of names of dump providers that are blacklisted from strict thread | 57 // The list of names of dump providers that are blacklisted from strict thread | 
| 61 // affinity check on unregistration. These providers could potentially cause | 58 // affinity check on unregistration. These providers could potentially cause | 
| 62 // crashes on build bots if they do not unregister on right thread. | 59 // crashes on build bots if they do not unregister on right thread. | 
| 63 // TODO(ssid): Fix all the dump providers to unregister if needed and clear the | 60 // TODO(ssid): Fix all the dump providers to unregister if needed and clear the | 
| 64 // blacklist, crbug.com/643438. | 61 // blacklist, crbug.com/643438. | 
| 65 const char* const kStrictThreadCheckBlacklist[] = { | 62 const char* const kStrictThreadCheckBlacklist[] = { | 
| 66     "ClientDiscardableSharedMemoryManager", | 63     "ClientDiscardableSharedMemoryManager", | 
| (...skipping 125 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 192 | 189 | 
| 193   // At this point the command line may not be initialized but we try to | 190   // At this point the command line may not be initialized but we try to | 
| 194   // enable the heap profiler to capture allocations as soon as possible. | 191   // enable the heap profiler to capture allocations as soon as possible. | 
| 195   EnableHeapProfilingIfNeeded(); | 192   EnableHeapProfilingIfNeeded(); | 
| 196 | 193 | 
| 197   strict_thread_check_blacklist_.insert(std::begin(kStrictThreadCheckBlacklist), | 194   strict_thread_check_blacklist_.insert(std::begin(kStrictThreadCheckBlacklist), | 
| 198                                         std::end(kStrictThreadCheckBlacklist)); | 195                                         std::end(kStrictThreadCheckBlacklist)); | 
| 199 } | 196 } | 
| 200 | 197 | 
| 201 MemoryDumpManager::~MemoryDumpManager() { | 198 MemoryDumpManager::~MemoryDumpManager() { | 
| 202   TraceLog::GetInstance()->RemoveEnabledStateObserver(this); |  | 
| 203 } | 199 } | 
| 204 | 200 | 
| 205 void MemoryDumpManager::EnableHeapProfilingIfNeeded() { | 201 void MemoryDumpManager::EnableHeapProfilingIfNeeded() { | 
| 206   if (heap_profiling_enabled_) | 202   if (heap_profiling_enabled_) | 
| 207     return; | 203     return; | 
| 208 | 204 | 
| 209   if (!CommandLine::InitializedForCurrentProcess() || | 205   if (!CommandLine::InitializedForCurrentProcess() || | 
| 210       !CommandLine::ForCurrentProcess()->HasSwitch( | 206       !CommandLine::ForCurrentProcess()->HasSwitch( | 
| 211           switches::kEnableHeapProfiling)) | 207           switches::kEnableHeapProfiling)) | 
| 212     return; | 208     return; | 
| (...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 283 | 279 | 
| 284     TraceConfig::EventFilters filters; | 280     TraceConfig::EventFilters filters; | 
| 285     filters.push_back(heap_profiler_filter_config); | 281     filters.push_back(heap_profiler_filter_config); | 
| 286     TraceConfig filtering_trace_config; | 282     TraceConfig filtering_trace_config; | 
| 287     filtering_trace_config.SetEventFilters(filters); | 283     filtering_trace_config.SetEventFilters(filters); | 
| 288 | 284 | 
| 289     TraceLog::GetInstance()->SetEnabled(filtering_trace_config, | 285     TraceLog::GetInstance()->SetEnabled(filtering_trace_config, | 
| 290                                         TraceLog::FILTERING_MODE); | 286                                         TraceLog::FILTERING_MODE); | 
| 291   } | 287   } | 
| 292 | 288 | 
| 293   // If tracing was enabled before initializing MemoryDumpManager, we missed the | 289   // TODO(hjd): Move out of MDM. See: crbug.com/703184 | 
| 294   // OnTraceLogEnabled() event. Synthetize it so we can late-join the party. | 290   tracing_observer_ = | 
| 295   // IsEnabled is called before adding observer to avoid calling | 291       MakeUnique<MemoryTracingObserver>(TraceLog::GetInstance(), this); | 
| 296   // OnTraceLogEnabled twice. |  | 
| 297   bool is_tracing_already_enabled = TraceLog::GetInstance()->IsEnabled(); |  | 
| 298   TraceLog::GetInstance()->AddEnabledStateObserver(this); |  | 
| 299   if (is_tracing_already_enabled) |  | 
| 300     OnTraceLogEnabled(); |  | 
| 301 } | 292 } | 
| 302 | 293 | 
| 303 void MemoryDumpManager::RegisterDumpProvider( | 294 void MemoryDumpManager::RegisterDumpProvider( | 
| 304     MemoryDumpProvider* mdp, | 295     MemoryDumpProvider* mdp, | 
| 305     const char* name, | 296     const char* name, | 
| 306     scoped_refptr<SingleThreadTaskRunner> task_runner, | 297     scoped_refptr<SingleThreadTaskRunner> task_runner, | 
| 307     MemoryDumpProvider::Options options) { | 298     MemoryDumpProvider::Options options) { | 
| 308   options.dumps_on_single_thread_task_runner = true; | 299   options.dumps_on_single_thread_task_runner = true; | 
| 309   RegisterDumpProviderInternal(mdp, name, std::move(task_runner), options); | 300   RegisterDumpProviderInternal(mdp, name, std::move(task_runner), options); | 
| 310 } | 301 } | 
| (...skipping 405 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 716                                           const ProcessMemoryDump* pmd) { | 707                                           const ProcessMemoryDump* pmd) { | 
| 717   uint64_t sum = 0; | 708   uint64_t sum = 0; | 
| 718   for (const auto& kv : pmd->allocator_dumps()) { | 709   for (const auto& kv : pmd->allocator_dumps()) { | 
| 719     auto name = StringPiece(kv.first); | 710     auto name = StringPiece(kv.first); | 
| 720     if (MatchPattern(name, pattern)) | 711     if (MatchPattern(name, pattern)) | 
| 721       sum += kv.second->GetSize(); | 712       sum += kv.second->GetSize(); | 
| 722   } | 713   } | 
| 723   return sum / 1024; | 714   return sum / 1024; | 
| 724 } | 715 } | 
| 725 | 716 | 
| 726 // static |  | 
| 727 void MemoryDumpManager::FinalizeDumpAndAddToTrace( | 717 void MemoryDumpManager::FinalizeDumpAndAddToTrace( | 
| 728     std::unique_ptr<ProcessMemoryDumpAsyncState> pmd_async_state) { | 718     std::unique_ptr<ProcessMemoryDumpAsyncState> pmd_async_state) { | 
| 729   HEAP_PROFILER_SCOPED_IGNORE; | 719   HEAP_PROFILER_SCOPED_IGNORE; | 
| 730   DCHECK(pmd_async_state->pending_dump_providers.empty()); | 720   DCHECK(pmd_async_state->pending_dump_providers.empty()); | 
| 731   const uint64_t dump_guid = pmd_async_state->req_args.dump_guid; | 721   const uint64_t dump_guid = pmd_async_state->req_args.dump_guid; | 
| 732   if (!pmd_async_state->callback_task_runner->BelongsToCurrentThread()) { | 722   if (!pmd_async_state->callback_task_runner->BelongsToCurrentThread()) { | 
| 733     scoped_refptr<SingleThreadTaskRunner> callback_task_runner = | 723     scoped_refptr<SingleThreadTaskRunner> callback_task_runner = | 
| 734         pmd_async_state->callback_task_runner; | 724         pmd_async_state->callback_task_runner; | 
| 735     callback_task_runner->PostTask( | 725     callback_task_runner->PostTask( | 
| 736         FROM_HERE, BindOnce(&MemoryDumpManager::FinalizeDumpAndAddToTrace, | 726         FROM_HERE, BindOnce(&MemoryDumpManager::FinalizeDumpAndAddToTrace, | 
| 737                             Passed(&pmd_async_state))); | 727                             Unretained(this), Passed(&pmd_async_state))); | 
| 738     return; | 728     return; | 
| 739   } | 729   } | 
| 740 | 730 | 
| 741   TRACE_EVENT0(kTraceCategory, "MemoryDumpManager::FinalizeDumpAndAddToTrace"); | 731   TRACE_EVENT0(kTraceCategory, "MemoryDumpManager::FinalizeDumpAndAddToTrace"); | 
| 742 | 732 | 
| 743   // The results struct to fill. | 733   // The results struct to fill. | 
| 744   // TODO(hjd): Transitional until we send the full PMD. See crbug.com/704203 | 734   // TODO(hjd): Transitional until we send the full PMD. See crbug.com/704203 | 
| 745   base::Optional<MemoryDumpCallbackResult> result; | 735   base::Optional<MemoryDumpCallbackResult> result; | 
|  | 736 | 
|  | 737   bool dump_successful = pmd_async_state->dump_successful; | 
|  | 738 | 
| 746   for (const auto& kv : pmd_async_state->process_dumps) { | 739   for (const auto& kv : pmd_async_state->process_dumps) { | 
| 747     ProcessId pid = kv.first;  // kNullProcessId for the current process. | 740     ProcessId pid = kv.first;  // kNullProcessId for the current process. | 
| 748     ProcessMemoryDump* process_memory_dump = kv.second.get(); | 741     ProcessMemoryDump* process_memory_dump = kv.second.get(); | 
| 749     std::unique_ptr<TracedValue> traced_value(new TracedValue); |  | 
| 750     process_memory_dump->AsValueInto(traced_value.get()); |  | 
| 751     traced_value->SetString("level_of_detail", |  | 
| 752                             MemoryDumpLevelOfDetailToString( |  | 
| 753                                 pmd_async_state->req_args.level_of_detail)); |  | 
| 754     const char* const event_name = |  | 
| 755         MemoryDumpTypeToString(pmd_async_state->req_args.dump_type); |  | 
| 756 | 742 | 
| 757     std::unique_ptr<ConvertableToTraceFormat> event_value( | 743     bool added_to_trace = tracing_observer_->AddDumpToTraceIfEnabled( | 
| 758         std::move(traced_value)); | 744         &pmd_async_state->req_args, pid, process_memory_dump); | 
| 759     TRACE_EVENT_API_ADD_TRACE_EVENT_WITH_PROCESS_ID( | 745 | 
| 760         TRACE_EVENT_PHASE_MEMORY_DUMP, | 746     dump_successful = dump_successful && added_to_trace; | 
| 761         TraceLog::GetCategoryGroupEnabled(kTraceCategory), event_name, |  | 
| 762         trace_event_internal::kGlobalScope, dump_guid, pid, |  | 
| 763         kTraceEventNumArgs, kTraceEventArgNames, |  | 
| 764         kTraceEventArgTypes, nullptr /* arg_values */, &event_value, |  | 
| 765         TRACE_EVENT_FLAG_HAS_ID); |  | 
| 766 | 747 | 
| 767     // TODO(hjd): Transitional until we send the full PMD. See crbug.com/704203 | 748     // TODO(hjd): Transitional until we send the full PMD. See crbug.com/704203 | 
| 768     // Don't try to fill the struct in detailed mode since it is hard to avoid | 749     // Don't try to fill the struct in detailed mode since it is hard to avoid | 
| 769     // double counting. | 750     // double counting. | 
| 770     if (pmd_async_state->req_args.level_of_detail == | 751     if (pmd_async_state->req_args.level_of_detail == | 
| 771         MemoryDumpLevelOfDetail::DETAILED) | 752         MemoryDumpLevelOfDetail::DETAILED) | 
| 772       continue; | 753       continue; | 
| 773     if (!result.has_value()) | 754     if (!result.has_value()) | 
| 774       result = MemoryDumpCallbackResult(); | 755       result = MemoryDumpCallbackResult(); | 
| 775     // TODO(hjd): Transitional until we send the full PMD. See crbug.com/704203 | 756     // TODO(hjd): Transitional until we send the full PMD. See crbug.com/704203 | 
| (...skipping 11 matching lines...) Expand all  Loading... | 
| 787           GetDumpsSumKb("partition_alloc/partitions/*", process_memory_dump); | 768           GetDumpsSumKb("partition_alloc/partitions/*", process_memory_dump); | 
| 788       result->chrome_dump.blink_gc_total_kb = | 769       result->chrome_dump.blink_gc_total_kb = | 
| 789           GetDumpsSumKb("blink_gc", process_memory_dump); | 770           GetDumpsSumKb("blink_gc", process_memory_dump); | 
| 790       FillOsDumpFromProcessMemoryDump(process_memory_dump, &result->os_dump); | 771       FillOsDumpFromProcessMemoryDump(process_memory_dump, &result->os_dump); | 
| 791     } else { | 772     } else { | 
| 792       auto& os_dump = result->extra_processes_dump[pid]; | 773       auto& os_dump = result->extra_processes_dump[pid]; | 
| 793       FillOsDumpFromProcessMemoryDump(process_memory_dump, &os_dump); | 774       FillOsDumpFromProcessMemoryDump(process_memory_dump, &os_dump); | 
| 794     } | 775     } | 
| 795   } | 776   } | 
| 796 | 777 | 
| 797   bool tracing_still_enabled; |  | 
| 798   TRACE_EVENT_CATEGORY_GROUP_ENABLED(kTraceCategory, &tracing_still_enabled); |  | 
| 799   if (!tracing_still_enabled) { |  | 
| 800     pmd_async_state->dump_successful = false; |  | 
| 801     VLOG(1) << kLogPrefix << " failed because tracing was disabled before" |  | 
| 802             << " the dump was completed"; |  | 
| 803   } |  | 
| 804 |  | 
| 805   if (!pmd_async_state->callback.is_null()) { | 778   if (!pmd_async_state->callback.is_null()) { | 
| 806     pmd_async_state->callback.Run(dump_guid, pmd_async_state->dump_successful, | 779     pmd_async_state->callback.Run(dump_guid, dump_successful, result); | 
| 807                                   result); |  | 
| 808     pmd_async_state->callback.Reset(); | 780     pmd_async_state->callback.Reset(); | 
| 809   } | 781   } | 
| 810 | 782 | 
| 811   TRACE_EVENT_NESTABLE_ASYNC_END0(kTraceCategory, "ProcessMemoryDump", | 783   TRACE_EVENT_NESTABLE_ASYNC_END0(kTraceCategory, "ProcessMemoryDump", | 
| 812                                   TRACE_ID_LOCAL(dump_guid)); | 784                                   TRACE_ID_LOCAL(dump_guid)); | 
| 813 } | 785 } | 
| 814 | 786 | 
| 815 void MemoryDumpManager::OnTraceLogEnabled() { | 787 void MemoryDumpManager::Enable( | 
| 816   bool enabled; | 788     const TraceConfig::MemoryDumpConfig& memory_dump_config) { | 
| 817   TRACE_EVENT_CATEGORY_GROUP_ENABLED(kTraceCategory, &enabled); |  | 
| 818   if (!enabled) |  | 
| 819     return; |  | 
| 820 |  | 
| 821   // Initialize the TraceLog for the current thread. This is to avoid that the |  | 
| 822   // TraceLog memory dump provider is registered lazily in the PostTask() below |  | 
| 823   // while the |lock_| is taken; |  | 
| 824   TraceLog::GetInstance()->InitializeThreadLocalEventBufferIfSupported(); |  | 
| 825 |  | 
| 826   // Spin-up the thread used to invoke unbound dump providers. | 789   // Spin-up the thread used to invoke unbound dump providers. | 
| 827   std::unique_ptr<Thread> dump_thread(new Thread("MemoryInfra")); | 790   std::unique_ptr<Thread> dump_thread(new Thread("MemoryInfra")); | 
| 828   if (!dump_thread->Start()) { | 791   if (!dump_thread->Start()) { | 
| 829     LOG(ERROR) << "Failed to start the memory-infra thread for tracing"; | 792     LOG(ERROR) << "Failed to start the memory-infra thread for tracing"; | 
| 830     return; | 793     return; | 
| 831   } | 794   } | 
| 832 | 795 | 
| 833   const TraceConfig& trace_config = |  | 
| 834       TraceLog::GetInstance()->GetCurrentTraceConfig(); |  | 
| 835   const TraceConfig::MemoryDumpConfig& memory_dump_config = |  | 
| 836       trace_config.memory_dump_config(); |  | 
| 837   scoped_refptr<MemoryDumpSessionState> session_state = | 796   scoped_refptr<MemoryDumpSessionState> session_state = | 
| 838       new MemoryDumpSessionState; | 797       new MemoryDumpSessionState; | 
| 839   session_state->SetAllowedDumpModes(memory_dump_config.allowed_dump_modes); | 798   session_state->SetAllowedDumpModes(memory_dump_config.allowed_dump_modes); | 
| 840   session_state->set_heap_profiler_breakdown_threshold_bytes( | 799   session_state->set_heap_profiler_breakdown_threshold_bytes( | 
| 841       memory_dump_config.heap_profiler_options.breakdown_threshold_bytes); | 800       memory_dump_config.heap_profiler_options.breakdown_threshold_bytes); | 
| 842   if (heap_profiling_enabled_) { | 801   if (heap_profiling_enabled_) { | 
| 843     // If heap profiling is enabled, the stack frame deduplicator and type name | 802     // If heap profiling is enabled, the stack frame deduplicator and type name | 
| 844     // deduplicator will be in use. Add a metadata events to write the frames | 803     // deduplicator will be in use. Add a metadata events to write the frames | 
| 845     // and type IDs. | 804     // and type IDs. | 
| 846     session_state->SetStackFrameDeduplicator( | 805     session_state->SetStackFrameDeduplicator( | 
| (...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 911     } | 870     } | 
| 912   } | 871   } | 
| 913 | 872 | 
| 914   // Only coordinator process triggers periodic global memory dumps. | 873   // Only coordinator process triggers periodic global memory dumps. | 
| 915   if (delegate_->IsCoordinator() && !periodic_config.triggers.empty()) { | 874   if (delegate_->IsCoordinator() && !periodic_config.triggers.empty()) { | 
| 916     MemoryDumpScheduler::GetInstance()->Start(periodic_config, | 875     MemoryDumpScheduler::GetInstance()->Start(periodic_config, | 
| 917                                               dump_thread_->task_runner()); | 876                                               dump_thread_->task_runner()); | 
| 918   } | 877   } | 
| 919 } | 878 } | 
| 920 | 879 | 
| 921 void MemoryDumpManager::OnTraceLogDisabled() { | 880 void MemoryDumpManager::Disable() { | 
| 922   // There might be a memory dump in progress while this happens. Therefore, | 881   // There might be a memory dump in progress while this happens. Therefore, | 
| 923   // ensure that the MDM state which depends on the tracing enabled / disabled | 882   // ensure that the MDM state which depends on the tracing enabled / disabled | 
| 924   // state is always accessed by the dumping methods holding the |lock_|. | 883   // state is always accessed by the dumping methods holding the |lock_|. | 
| 925   if (!subtle::NoBarrier_Load(&memory_tracing_enabled_)) | 884   if (!subtle::NoBarrier_Load(&memory_tracing_enabled_)) | 
| 926     return; | 885     return; | 
| 927   subtle::NoBarrier_Store(&memory_tracing_enabled_, 0); | 886   subtle::NoBarrier_Store(&memory_tracing_enabled_, 0); | 
| 928   std::unique_ptr<Thread> dump_thread; | 887   std::unique_ptr<Thread> dump_thread; | 
| 929   { | 888   { | 
| 930     AutoLock lock(lock_); | 889     AutoLock lock(lock_); | 
| 931     MemoryDumpScheduler::GetInstance()->Stop(); | 890     MemoryDumpScheduler::GetInstance()->Stop(); | 
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 973   if (iter == process_dumps.end()) { | 932   if (iter == process_dumps.end()) { | 
| 974     std::unique_ptr<ProcessMemoryDump> new_pmd( | 933     std::unique_ptr<ProcessMemoryDump> new_pmd( | 
| 975         new ProcessMemoryDump(session_state, dump_args)); | 934         new ProcessMemoryDump(session_state, dump_args)); | 
| 976     iter = process_dumps.insert(std::make_pair(pid, std::move(new_pmd))).first; | 935     iter = process_dumps.insert(std::make_pair(pid, std::move(new_pmd))).first; | 
| 977   } | 936   } | 
| 978   return iter->second.get(); | 937   return iter->second.get(); | 
| 979 } | 938 } | 
| 980 | 939 | 
| 981 }  // namespace trace_event | 940 }  // namespace trace_event | 
| 982 }  // namespace base | 941 }  // namespace base | 
| OLD | NEW | 
|---|