diff options
author | Hidehiko Abe <hidehiko@google.com> | 2018-06-04 10:22:00 -0700 |
---|---|---|
committer | android-build-merger <android-build-merger@google.com> | 2018-06-04 10:22:00 -0700 |
commit | e2c1a37558287288715a7f1bdb73ac9e66d1dff1 (patch) | |
tree | 3c20b00d2e41637aceb9bd58acc79a0ec787922a | |
parent | b5e42b358cbe884964bdcb710a8037cc6038d68a (diff) | |
parent | ad62a8245af1d123a106c870203a777260d231f8 (diff) | |
download | libchrome-e2c1a37558287288715a7f1bdb73ac9e66d1dff1.tar.gz |
Remove trace_event.
am: ad62a8245a
Change-Id: I671033cdf0bcb8c67b657127cbfc5f77a211565a
101 files changed, 268 insertions, 23994 deletions
diff --git a/Android.bp b/Android.bp index 55efa6a35f..b4bb28feb7 100644 --- a/Android.bp +++ b/Android.bp @@ -191,8 +191,6 @@ libchromeCommonSrc = [ "base/process/process_metrics.cc", "base/process/process_metrics_posix.cc", "base/process/process_posix.cc", - "base/profiler/scoped_profile.cc", - "base/profiler/scoped_tracker.cc", "base/profiler/tracked_time.cc", "base/rand_util.cc", "base/rand_util_posix.cc", @@ -256,39 +254,6 @@ libchromeCommonSrc = [ "base/time/time_posix.cc", "base/timer/elapsed_timer.cc", "base/timer/timer.cc", - "base/trace_event/category_registry.cc", - "base/trace_event/event_name_filter.cc", - "base/trace_event/heap_profiler_allocation_context.cc", - "base/trace_event/heap_profiler_allocation_context_tracker.cc", - "base/trace_event/heap_profiler_allocation_register.cc", - "base/trace_event/heap_profiler_allocation_register_posix.cc", - "base/trace_event/heap_profiler_event_filter.cc", - "base/trace_event/heap_profiler_heap_dump_writer.cc", - "base/trace_event/heap_profiler_stack_frame_deduplicator.cc", - "base/trace_event/heap_profiler_type_name_deduplicator.cc", - "base/trace_event/malloc_dump_provider.cc", - "base/trace_event/memory_allocator_dump.cc", - "base/trace_event/memory_allocator_dump_guid.cc", - "base/trace_event/memory_dump_manager.cc", - "base/trace_event/memory_dump_provider_info.cc", - "base/trace_event/memory_dump_request_args.cc", - "base/trace_event/memory_dump_scheduler.cc", - "base/trace_event/memory_dump_session_state.cc", - "base/trace_event/memory_infra_background_whitelist.cc", - "base/trace_event/memory_usage_estimator.cc", - "base/trace_event/process_memory_dump.cc", - "base/trace_event/process_memory_maps.cc", - "base/trace_event/process_memory_totals.cc", - "base/trace_event/trace_buffer.cc", - "base/trace_event/trace_config.cc", - "base/trace_event/trace_config_category_filter.cc", - "base/trace_event/trace_event_argument.cc", - "base/trace_event/trace_event_filter.cc", - "base/trace_event/trace_event_impl.cc", - "base/trace_event/trace_event_memory_overhead.cc", - "base/trace_event/trace_event_synthetic_delay.cc", - "base/trace_event/trace_log.cc", - "base/trace_event/trace_log_constants.cc", "base/tracked_objects.cc", "base/tracking_info.cc", "base/unguessable_token.cc", @@ -318,7 +283,6 @@ libchromeLinuxSrc = [ "base/files/file_path_watcher_linux.cc", "base/files/file_util_linux.cc", "base/memory/shared_memory_posix.cc", - "base/memory/shared_memory_tracker.cc", "base/posix/unix_domain_socket_linux.cc", "base/process/internal_linux.cc", "base/process/memory_linux.cc", @@ -571,7 +535,6 @@ cc_test { "base/test/test_simple_task_runner.cc", "base/test/test_switches.cc", "base/test/test_timeouts.cc", - "base/test/trace_event_analyzer.cc", "base/threading/non_thread_safe_unittest.cc", "base/threading/platform_thread_unittest.cc", "base/threading/simple_thread_unittest.cc", @@ -587,19 +550,6 @@ cc_test { "base/time/time_unittest.cc", "base/timer/hi_res_timer_manager_unittest.cc", "base/timer/timer_unittest.cc", - "base/trace_event/event_name_filter_unittest.cc", - "base/trace_event/heap_profiler_allocation_context_tracker_unittest.cc", - "base/trace_event/heap_profiler_stack_frame_deduplicator_unittest.cc", - "base/trace_event/heap_profiler_type_name_deduplicator_unittest.cc", - "base/trace_event/memory_allocator_dump_unittest.cc", - "base/trace_event/memory_dump_manager_unittest.cc", - "base/trace_event/memory_usage_estimator_unittest.cc", - "base/trace_event/process_memory_dump_unittest.cc", - "base/trace_event/trace_config_unittest.cc", - "base/trace_event/trace_event_argument_unittest.cc", - "base/trace_event/trace_event_filter_test_utils.cc", - "base/trace_event/trace_event_synthetic_delay_unittest.cc", - "base/trace_event/trace_event_unittest.cc", "base/tracked_objects_unittest.cc", "base/tuple_unittest.cc", "base/values_unittest.cc", diff --git a/base/memory/shared_memory_posix.cc b/base/memory/shared_memory_posix.cc index 287e55d823..3443cd9b4a 100644 --- a/base/memory/shared_memory_posix.cc +++ b/base/memory/shared_memory_posix.cc @@ -15,7 +15,8 @@ #include "base/files/scoped_file.h" #include "base/logging.h" #include "base/memory/shared_memory_helper.h" -#include "base/memory/shared_memory_tracker.h" +// Unsupported in libchrome. +// #include "base/memory/shared_memory_tracker.h" #include "base/posix/eintr_wrapper.h" #include "base/posix/safe_strerror.h" #include "base/process/process_metrics.h" @@ -290,7 +291,8 @@ bool SharedMemory::MapAt(off_t offset, size_t bytes) { DCHECK_EQ(0U, reinterpret_cast<uintptr_t>(memory_) & (SharedMemory::MAP_MINIMUM_ALIGNMENT - 1)); - SharedMemoryTracker::GetInstance()->IncrementMemoryUsage(*this); + // Unsupported in libchrome. + // SharedMemoryTracker::GetInstance()->IncrementMemoryUsage(*this); } else { memory_ = NULL; } @@ -303,7 +305,8 @@ bool SharedMemory::Unmap() { return false; munmap(memory_, mapped_size_); - SharedMemoryTracker::GetInstance()->DecrementMemoryUsage(*this); + // Unsupported in libchrome. + // SharedMemoryTracker::GetInstance()->DecrementMemoryUsage(*this); memory_ = NULL; mapped_size_ = 0; return true; diff --git a/base/memory/shared_memory_tracker.cc b/base/memory/shared_memory_tracker.cc deleted file mode 100644 index 8613f59533..0000000000 --- a/base/memory/shared_memory_tracker.cc +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/memory/shared_memory_tracker.h" - -#include "base/memory/shared_memory.h" -#include "base/strings/stringprintf.h" -#include "base/trace_event/memory_dump_manager.h" -#include "base/trace_event/process_memory_dump.h" - -namespace base { - -SharedMemoryTracker::Usage::Usage() = default; - -SharedMemoryTracker::Usage::Usage(const Usage& rhs) = default; - -SharedMemoryTracker::Usage::~Usage() = default; - -// static -SharedMemoryTracker* SharedMemoryTracker::GetInstance() { - static SharedMemoryTracker* instance = new SharedMemoryTracker; - return instance; -} - -void SharedMemoryTracker::IncrementMemoryUsage( - const SharedMemory& shared_memory) { - Usage usage; - // |shared_memory|'s unique ID must be generated here and it'd be too late at - // OnMemoryDump. An ID is generated with a SharedMemoryHandle, but the handle - // might already be closed at that time. Now IncrementMemoryUsage is called - // just after mmap and the handle must live then. See the discussion at - // crbug.com/604726#c30. - SharedMemory::UniqueId id; - if (!shared_memory.GetUniqueId(&id)) - return; - usage.unique_id = id; - usage.size = shared_memory.mapped_size(); - AutoLock hold(usages_lock_); - usages_[&shared_memory] = usage; -} - -void SharedMemoryTracker::DecrementMemoryUsage( - const SharedMemory& shared_memory) { - AutoLock hold(usages_lock_); - usages_.erase(&shared_memory); -} - -bool SharedMemoryTracker::OnMemoryDump(const trace_event::MemoryDumpArgs& args, - trace_event::ProcessMemoryDump* pmd) { - std::unordered_map<SharedMemory::UniqueId, size_t, SharedMemory::UniqueIdHash> - sizes; - { - AutoLock hold(usages_lock_); - for (const auto& usage : usages_) - sizes[usage.second.unique_id] += usage.second.size; - } - for (auto& size : sizes) { - const SharedMemory::UniqueId& id = size.first; - std::string dump_name = StringPrintf("%s/%lld.%lld", "shared_memory", - static_cast<long long>(id.first), - static_cast<long long>(id.second)); - auto guid = trace_event::MemoryAllocatorDumpGuid(dump_name); - trace_event::MemoryAllocatorDump* local_dump = - pmd->CreateAllocatorDump(dump_name); - // TODO(hajimehoshi): The size is not resident size but virtual size so far. - // Fix this to record resident size. - local_dump->AddScalar(trace_event::MemoryAllocatorDump::kNameSize, - trace_event::MemoryAllocatorDump::kUnitsBytes, - size.second); - trace_event::MemoryAllocatorDump* global_dump = - pmd->CreateSharedGlobalAllocatorDump(guid); - global_dump->AddScalar(trace_event::MemoryAllocatorDump::kNameSize, - trace_event::MemoryAllocatorDump::kUnitsBytes, - size.second); - // TOOD(hajimehoshi): Detect which the shared memory comes from browser, - // renderer or GPU process. - // TODO(hajimehoshi): Shared memory reported by GPU and discardable is - // currently double-counted. Add ownership edges to avoid this. - pmd->AddOwnershipEdge(local_dump->guid(), global_dump->guid()); - } - return true; -} - -SharedMemoryTracker::SharedMemoryTracker() { - trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider( - this, "SharedMemoryTracker", nullptr); -} - -SharedMemoryTracker::~SharedMemoryTracker() = default; - -} // namespace diff --git a/base/memory/shared_memory_tracker.h b/base/memory/shared_memory_tracker.h deleted file mode 100644 index fe1a3dd392..0000000000 --- a/base/memory/shared_memory_tracker.h +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_MEMORY_SHARED_MEMORY_TRACKER_H_ -#define BASE_MEMORY_SHARED_MEMORY_TRACKER_H_ - -#include "base/memory/shared_memory.h" -#include "base/synchronization/lock.h" -#include "base/trace_event/memory_dump_provider.h" - -namespace base { - -namespace trace_event { -class ProcessMemoryDump; -} - -// SharedMemoryTracker tracks shared memory usage. -class BASE_EXPORT SharedMemoryTracker - : public base::trace_event::MemoryDumpProvider { - public: - // Returns a singleton instance. - static SharedMemoryTracker* GetInstance(); - - // Records shared memory usage on mapping. - void IncrementMemoryUsage(const SharedMemory& shared_memory); - - // Records shared memory usage on unmapping. - void DecrementMemoryUsage(const SharedMemory& shared_memory); - - private: - struct Usage { - Usage(); - Usage(const Usage& rhs); - ~Usage(); - SharedMemory::UniqueId unique_id; - size_t size; - }; - - SharedMemoryTracker(); - ~SharedMemoryTracker() override; - - // base::trace_event::MemoryDumpProvider implementation. - bool OnMemoryDump(const base::trace_event::MemoryDumpArgs& args, - base::trace_event::ProcessMemoryDump* pmd) override; - - // Used to lock when |usages_| is modified or read. - Lock usages_lock_; - std::unordered_map<const SharedMemory*, Usage> usages_; - - DISALLOW_COPY_AND_ASSIGN(SharedMemoryTracker); -}; - -} // namespace base - -#endif // BASE_MEMORY_SHARED_MEMORY_TRACKER_H_ diff --git a/base/profiler/scoped_profile.cc b/base/profiler/scoped_profile.cc deleted file mode 100644 index f06a8c6f5d..0000000000 --- a/base/profiler/scoped_profile.cc +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/profiler/scoped_profile.h" - -#include "base/location.h" -#include "base/tracked_objects.h" - - -namespace tracked_objects { - - -ScopedProfile::ScopedProfile(const Location& location, Mode mode) - : birth_(NULL) { - if (mode == DISABLED) - return; - - birth_ = ThreadData::TallyABirthIfActive(location); - if (!birth_) - return; - - stopwatch_.Start(); -} - -ScopedProfile::~ScopedProfile() { - if (!birth_) - return; - - stopwatch_.Stop(); - ThreadData::TallyRunInAScopedRegionIfTracking(birth_, stopwatch_); -} - -} // namespace tracked_objects diff --git a/base/profiler/scoped_profile.h b/base/profiler/scoped_profile.h deleted file mode 100644 index 4df6a1bc02..0000000000 --- a/base/profiler/scoped_profile.h +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - - -#ifndef BASE_PROFILER_SCOPED_PROFILE_H_ -#define BASE_PROFILER_SCOPED_PROFILE_H_ - -//------------------------------------------------------------------------------ -// ScopedProfile provides basic helper functions for profiling a short -// region of code within a scope. It is separate from the related ThreadData -// class so that it can be included without much other cruft, and provide the -// macros listed below. - -#include "base/base_export.h" -#include "base/location.h" -#include "base/macros.h" -#include "base/profiler/tracked_time.h" -#include "base/trace_event/heap_profiler.h" -#include "base/tracked_objects.h" - -// Two level indirection is required for correct macro substitution. -#define PASTE_COUNTER_ON_NAME2(name, counter) name##counter -#define PASTE_COUNTER_ON_NAME(name, counter) \ - PASTE_COUNTER_ON_NAME2(name, counter) - -#define COUNTER_BASED_VARIABLE_NAME_FOR_PROFILING \ - PASTE_COUNTER_ON_NAME(some_profiler_variable_, __COUNTER__) - -// Defines the containing scope as a profiled region. This allows developers to -// profile their code and see results on their about:profiler page, as well as -// on the UMA dashboard and heap profiler. -#define TRACK_RUN_IN_THIS_SCOPED_REGION(dispatch_function_name) \ - const ::tracked_objects::Location& location = \ - FROM_HERE_WITH_EXPLICIT_FUNCTION(#dispatch_function_name); \ - TRACE_HEAP_PROFILER_API_SCOPED_TASK_EXECUTION \ - COUNTER_BASED_VARIABLE_NAME_FOR_PROFILING(location.file_name()); \ - ::tracked_objects::ScopedProfile COUNTER_BASED_VARIABLE_NAME_FOR_PROFILING( \ - location, ::tracked_objects::ScopedProfile::ENABLED) - -// Same as TRACK_RUN_IN_THIS_SCOPED_REGION except that there's an extra param -// which is concatenated with the function name for better filtering. -#define TRACK_SCOPED_REGION(category_name, dispatch_function_name) \ - const ::tracked_objects::Location& location = \ - FROM_HERE_WITH_EXPLICIT_FUNCTION("[" category_name \ - "]" dispatch_function_name); \ - TRACE_HEAP_PROFILER_API_SCOPED_TASK_EXECUTION \ - COUNTER_BASED_VARIABLE_NAME_FOR_PROFILING(location.file_name()); \ - ::tracked_objects::ScopedProfile COUNTER_BASED_VARIABLE_NAME_FOR_PROFILING( \ - location, ::tracked_objects::ScopedProfile::ENABLED) - -namespace tracked_objects { -class Births; - -class BASE_EXPORT ScopedProfile { - public: - // Mode of operation. Specifies whether ScopedProfile should be a no-op or - // needs to create and tally a task. - enum Mode { - DISABLED, // Do nothing. - ENABLED // Create and tally a task. - }; - - ScopedProfile(const Location& location, Mode mode); - ~ScopedProfile(); - - private: - Births* birth_; // Place in code where tracking started. - TaskStopwatch stopwatch_; - - DISALLOW_COPY_AND_ASSIGN(ScopedProfile); -}; - -} // namespace tracked_objects - -#endif // BASE_PROFILER_SCOPED_PROFILE_H_ diff --git a/base/profiler/scoped_tracker.cc b/base/profiler/scoped_tracker.cc deleted file mode 100644 index d15b7de6dc..0000000000 --- a/base/profiler/scoped_tracker.cc +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/profiler/scoped_tracker.h" - -#include "base/bind.h" - -namespace tracked_objects { - -namespace { - -ScopedProfile::Mode g_scoped_profile_mode = ScopedProfile::DISABLED; - -} // namespace - -// static -void ScopedTracker::Enable() { - g_scoped_profile_mode = ScopedProfile::ENABLED; -} - -ScopedTracker::ScopedTracker(const Location& location) - : scoped_profile_(location, g_scoped_profile_mode) { -} - -} // namespace tracked_objects diff --git a/base/profiler/scoped_tracker.h b/base/profiler/scoped_tracker.h deleted file mode 100644 index a61de9115c..0000000000 --- a/base/profiler/scoped_tracker.h +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_PROFILER_SCOPED_TRACKER_H_ -#define BASE_PROFILER_SCOPED_TRACKER_H_ - -//------------------------------------------------------------------------------ -// Utilities for temporarily instrumenting code to dig into issues that were -// found using profiler data. - -#include "base/base_export.h" -#include "base/bind.h" -#include "base/callback_forward.h" -#include "base/location.h" -#include "base/macros.h" -#include "base/profiler/scoped_profile.h" - -namespace tracked_objects { - -// ScopedTracker instruments a region within the code if the instrumentation is -// enabled. It can be used, for example, to find out if a source of jankiness is -// inside the instrumented code region. -// Details: -// 1. This class creates a task (like ones created by PostTask calls or IPC -// message handlers). This task can be seen in chrome://profiler and is sent as -// a part of profiler data to the UMA server. See profiler_event.proto. -// 2. That task's lifetime is same as the lifetime of the ScopedTracker -// instance. -// 3. The execution time associated with the task is the wallclock time between -// its constructor and destructor, minus wallclock times of directly nested -// tasks. -// 4. Task creation that this class utilizes is highly optimized. -// 5. The class doesn't create a task unless this was enabled for the current -// process. Search for ScopedTracker::Enable for the current list of processes -// and channels where it's activated. -// 6. The class is designed for temporarily instrumenting code to find -// performance problems, after which the instrumentation must be removed. -class BASE_EXPORT ScopedTracker { - public: - ScopedTracker(const Location& location); - - // Enables instrumentation for the remainder of the current process' life. If - // this function is not called, all profiler instrumentations are no-ops. - static void Enable(); - - // Augments a |callback| with provided |location|. This is useful for - // instrumenting cases when we know that a jank is in a callback and there are - // many possible callbacks, but they come from a relatively small number of - // places. We can instrument these few places and at least know which one - // passes the janky callback. - template <typename P1> - static base::Callback<void(P1)> TrackCallback( - const Location& location, - const base::Callback<void(P1)>& callback) { - return base::Bind(&ScopedTracker::ExecuteAndTrackCallback<P1>, location, - callback); - } - - private: - // Executes |callback|, augmenting it with provided |location|. - template <typename P1> - static void ExecuteAndTrackCallback(const Location& location, - const base::Callback<void(P1)>& callback, - P1 p1) { - ScopedTracker tracking_profile(location); - callback.Run(p1); - } - - const ScopedProfile scoped_profile_; - - DISALLOW_COPY_AND_ASSIGN(ScopedTracker); -}; - -} // namespace tracked_objects - -#endif // BASE_PROFILER_SCOPED_TRACKER_H_ diff --git a/base/test/test_pending_task.cc b/base/test/test_pending_task.cc index 3f71a9988f..fcc48a8980 100644 --- a/base/test/test_pending_task.cc +++ b/base/test/test_pending_task.cc @@ -38,6 +38,8 @@ bool TestPendingTask::ShouldRunBefore(const TestPendingTask& other) const { TestPendingTask::~TestPendingTask() {} +// Unsupported in libchrome. +#if 0 void TestPendingTask::AsValueInto(base::trace_event::TracedValue* state) const { state->SetInteger("run_at", GetTimeToRun().ToInternalValue()); state->SetString("posting_function", location.ToString()); @@ -61,10 +63,14 @@ TestPendingTask::AsValue() const { AsValueInto(state.get()); return std::move(state); } +#endif std::string TestPendingTask::ToString() const { std::string output("TestPendingTask("); +// Unsupported in libchrome. +#if 0 AsValue()->AppendAsTraceFormat(&output); +#endif output += ")"; return output; } diff --git a/base/test/test_pending_task.h b/base/test/test_pending_task.h index 52ca592f25..f8e8c798b8 100644 --- a/base/test/test_pending_task.h +++ b/base/test/test_pending_task.h @@ -10,7 +10,8 @@ #include "base/callback.h" #include "base/location.h" #include "base/time/time.h" -#include "base/trace_event/trace_event_argument.h" +// Unsupported in libchrome. +// #include "base/trace_event/trace_event_argument.h" namespace base { @@ -58,10 +59,13 @@ struct TestPendingTask { TimeDelta delay; TestNestability nestability; +// Unsupported in libchrome. +#if 0 // Functions for using test pending task with tracing, useful in unit // testing. void AsValueInto(base::trace_event::TracedValue* state) const; std::unique_ptr<base::trace_event::ConvertableToTraceFormat> AsValue() const; +#endif std::string ToString() const; private: diff --git a/base/test/trace_event_analyzer.cc b/base/test/trace_event_analyzer.cc deleted file mode 100644 index e61337cccb..0000000000 --- a/base/test/trace_event_analyzer.cc +++ /dev/null @@ -1,1027 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/test/trace_event_analyzer.h" - -#include <math.h> - -#include <algorithm> -#include <memory> -#include <set> - -#include "base/json/json_reader.h" -#include "base/strings/pattern.h" -#include "base/values.h" - -namespace trace_analyzer { - -// TraceEvent - -TraceEvent::TraceEvent() - : thread(0, 0), - timestamp(0), - duration(0), - phase(TRACE_EVENT_PHASE_BEGIN), - other_event(NULL) { -} - -TraceEvent::TraceEvent(TraceEvent&& other) = default; - -TraceEvent::~TraceEvent() { -} - -TraceEvent& TraceEvent::operator=(TraceEvent&& rhs) = default; - -bool TraceEvent::SetFromJSON(const base::Value* event_value) { - if (event_value->GetType() != base::Value::Type::DICTIONARY) { - LOG(ERROR) << "Value must be Type::DICTIONARY"; - return false; - } - const base::DictionaryValue* dictionary = - static_cast<const base::DictionaryValue*>(event_value); - - std::string phase_str; - const base::DictionaryValue* args = NULL; - - if (!dictionary->GetString("ph", &phase_str)) { - LOG(ERROR) << "ph is missing from TraceEvent JSON"; - return false; - } - - phase = *phase_str.data(); - - bool may_have_duration = (phase == TRACE_EVENT_PHASE_COMPLETE); - bool require_origin = (phase != TRACE_EVENT_PHASE_METADATA); - bool require_id = (phase == TRACE_EVENT_PHASE_ASYNC_BEGIN || - phase == TRACE_EVENT_PHASE_ASYNC_STEP_INTO || - phase == TRACE_EVENT_PHASE_ASYNC_STEP_PAST || - phase == TRACE_EVENT_PHASE_MEMORY_DUMP || - phase == TRACE_EVENT_PHASE_ENTER_CONTEXT || - phase == TRACE_EVENT_PHASE_LEAVE_CONTEXT || - phase == TRACE_EVENT_PHASE_CREATE_OBJECT || - phase == TRACE_EVENT_PHASE_DELETE_OBJECT || - phase == TRACE_EVENT_PHASE_SNAPSHOT_OBJECT || - phase == TRACE_EVENT_PHASE_ASYNC_END); - - if (require_origin && !dictionary->GetInteger("pid", &thread.process_id)) { - LOG(ERROR) << "pid is missing from TraceEvent JSON"; - return false; - } - if (require_origin && !dictionary->GetInteger("tid", &thread.thread_id)) { - LOG(ERROR) << "tid is missing from TraceEvent JSON"; - return false; - } - if (require_origin && !dictionary->GetDouble("ts", ×tamp)) { - LOG(ERROR) << "ts is missing from TraceEvent JSON"; - return false; - } - if (may_have_duration) { - dictionary->GetDouble("dur", &duration); - } - if (!dictionary->GetString("cat", &category)) { - LOG(ERROR) << "cat is missing from TraceEvent JSON"; - return false; - } - if (!dictionary->GetString("name", &name)) { - LOG(ERROR) << "name is missing from TraceEvent JSON"; - return false; - } - if (!dictionary->GetDictionary("args", &args)) { - LOG(ERROR) << "args is missing from TraceEvent JSON"; - return false; - } - if (require_id && !dictionary->GetString("id", &id)) { - LOG(ERROR) << "id is missing from ASYNC_BEGIN/ASYNC_END TraceEvent JSON"; - return false; - } - - // For each argument, copy the type and create a trace_analyzer::TraceValue. - for (base::DictionaryValue::Iterator it(*args); !it.IsAtEnd(); - it.Advance()) { - std::string str; - bool boolean = false; - int int_num = 0; - double double_num = 0.0; - if (it.value().GetAsString(&str)) { - arg_strings[it.key()] = str; - } else if (it.value().GetAsInteger(&int_num)) { - arg_numbers[it.key()] = static_cast<double>(int_num); - } else if (it.value().GetAsBoolean(&boolean)) { - arg_numbers[it.key()] = static_cast<double>(boolean ? 1 : 0); - } else if (it.value().GetAsDouble(&double_num)) { - arg_numbers[it.key()] = double_num; - } - // Record all arguments as values. - arg_values[it.key()] = it.value().CreateDeepCopy(); - } - - return true; -} - -double TraceEvent::GetAbsTimeToOtherEvent() const { - return fabs(other_event->timestamp - timestamp); -} - -bool TraceEvent::GetArgAsString(const std::string& name, - std::string* arg) const { - const auto it = arg_strings.find(name); - if (it != arg_strings.end()) { - *arg = it->second; - return true; - } - return false; -} - -bool TraceEvent::GetArgAsNumber(const std::string& name, - double* arg) const { - const auto it = arg_numbers.find(name); - if (it != arg_numbers.end()) { - *arg = it->second; - return true; - } - return false; -} - -bool TraceEvent::GetArgAsValue(const std::string& name, - std::unique_ptr<base::Value>* arg) const { - const auto it = arg_values.find(name); - if (it != arg_values.end()) { - *arg = it->second->CreateDeepCopy(); - return true; - } - return false; -} - -bool TraceEvent::HasStringArg(const std::string& name) const { - return (arg_strings.find(name) != arg_strings.end()); -} - -bool TraceEvent::HasNumberArg(const std::string& name) const { - return (arg_numbers.find(name) != arg_numbers.end()); -} - -bool TraceEvent::HasArg(const std::string& name) const { - return (arg_values.find(name) != arg_values.end()); -} - -std::string TraceEvent::GetKnownArgAsString(const std::string& name) const { - std::string arg_string; - bool result = GetArgAsString(name, &arg_string); - DCHECK(result); - return arg_string; -} - -double TraceEvent::GetKnownArgAsDouble(const std::string& name) const { - double arg_double = 0; - bool result = GetArgAsNumber(name, &arg_double); - DCHECK(result); - return arg_double; -} - -int TraceEvent::GetKnownArgAsInt(const std::string& name) const { - double arg_double = 0; - bool result = GetArgAsNumber(name, &arg_double); - DCHECK(result); - return static_cast<int>(arg_double); -} - -bool TraceEvent::GetKnownArgAsBool(const std::string& name) const { - double arg_double = 0; - bool result = GetArgAsNumber(name, &arg_double); - DCHECK(result); - return (arg_double != 0.0); -} - -std::unique_ptr<base::Value> TraceEvent::GetKnownArgAsValue( - const std::string& name) const { - std::unique_ptr<base::Value> arg_value; - bool result = GetArgAsValue(name, &arg_value); - DCHECK(result); - return arg_value; -} - -// QueryNode - -QueryNode::QueryNode(const Query& query) : query_(query) { -} - -QueryNode::~QueryNode() { -} - -// Query - -Query::Query(TraceEventMember member) - : type_(QUERY_EVENT_MEMBER), - operator_(OP_INVALID), - member_(member), - number_(0), - is_pattern_(false) { -} - -Query::Query(TraceEventMember member, const std::string& arg_name) - : type_(QUERY_EVENT_MEMBER), - operator_(OP_INVALID), - member_(member), - number_(0), - string_(arg_name), - is_pattern_(false) { -} - -Query::Query(const Query& query) - : type_(query.type_), - operator_(query.operator_), - left_(query.left_), - right_(query.right_), - member_(query.member_), - number_(query.number_), - string_(query.string_), - is_pattern_(query.is_pattern_) { -} - -Query::~Query() { -} - -Query Query::String(const std::string& str) { - return Query(str); -} - -Query Query::Double(double num) { - return Query(num); -} - -Query Query::Int(int32_t num) { - return Query(static_cast<double>(num)); -} - -Query Query::Uint(uint32_t num) { - return Query(static_cast<double>(num)); -} - -Query Query::Bool(bool boolean) { - return Query(boolean ? 1.0 : 0.0); -} - -Query Query::Phase(char phase) { - return Query(static_cast<double>(phase)); -} - -Query Query::Pattern(const std::string& pattern) { - Query query(pattern); - query.is_pattern_ = true; - return query; -} - -bool Query::Evaluate(const TraceEvent& event) const { - // First check for values that can convert to bool. - - // double is true if != 0: - double bool_value = 0.0; - bool is_bool = GetAsDouble(event, &bool_value); - if (is_bool) - return (bool_value != 0.0); - - // string is true if it is non-empty: - std::string str_value; - bool is_str = GetAsString(event, &str_value); - if (is_str) - return !str_value.empty(); - - DCHECK_EQ(QUERY_BOOLEAN_OPERATOR, type_) - << "Invalid query: missing boolean expression"; - DCHECK(left_.get()); - DCHECK(right_.get() || is_unary_operator()); - - if (is_comparison_operator()) { - DCHECK(left().is_value() && right().is_value()) - << "Invalid query: comparison operator used between event member and " - "value."; - bool compare_result = false; - if (CompareAsDouble(event, &compare_result)) - return compare_result; - if (CompareAsString(event, &compare_result)) - return compare_result; - return false; - } - // It's a logical operator. - switch (operator_) { - case OP_AND: - return left().Evaluate(event) && right().Evaluate(event); - case OP_OR: - return left().Evaluate(event) || right().Evaluate(event); - case OP_NOT: - return !left().Evaluate(event); - default: - NOTREACHED(); - return false; - } -} - -bool Query::CompareAsDouble(const TraceEvent& event, bool* result) const { - double lhs, rhs; - if (!left().GetAsDouble(event, &lhs) || !right().GetAsDouble(event, &rhs)) - return false; - switch (operator_) { - case OP_EQ: - *result = (lhs == rhs); - return true; - case OP_NE: - *result = (lhs != rhs); - return true; - case OP_LT: - *result = (lhs < rhs); - return true; - case OP_LE: - *result = (lhs <= rhs); - return true; - case OP_GT: - *result = (lhs > rhs); - return true; - case OP_GE: - *result = (lhs >= rhs); - return true; - default: - NOTREACHED(); - return false; - } -} - -bool Query::CompareAsString(const TraceEvent& event, bool* result) const { - std::string lhs, rhs; - if (!left().GetAsString(event, &lhs) || !right().GetAsString(event, &rhs)) - return false; - switch (operator_) { - case OP_EQ: - if (right().is_pattern_) - *result = base::MatchPattern(lhs, rhs); - else if (left().is_pattern_) - *result = base::MatchPattern(rhs, lhs); - else - *result = (lhs == rhs); - return true; - case OP_NE: - if (right().is_pattern_) - *result = !base::MatchPattern(lhs, rhs); - else if (left().is_pattern_) - *result = !base::MatchPattern(rhs, lhs); - else - *result = (lhs != rhs); - return true; - case OP_LT: - *result = (lhs < rhs); - return true; - case OP_LE: - *result = (lhs <= rhs); - return true; - case OP_GT: - *result = (lhs > rhs); - return true; - case OP_GE: - *result = (lhs >= rhs); - return true; - default: - NOTREACHED(); - return false; - } -} - -bool Query::EvaluateArithmeticOperator(const TraceEvent& event, - double* num) const { - DCHECK_EQ(QUERY_ARITHMETIC_OPERATOR, type_); - DCHECK(left_.get()); - DCHECK(right_.get() || is_unary_operator()); - - double lhs = 0, rhs = 0; - if (!left().GetAsDouble(event, &lhs)) - return false; - if (!is_unary_operator() && !right().GetAsDouble(event, &rhs)) - return false; - - switch (operator_) { - case OP_ADD: - *num = lhs + rhs; - return true; - case OP_SUB: - *num = lhs - rhs; - return true; - case OP_MUL: - *num = lhs * rhs; - return true; - case OP_DIV: - *num = lhs / rhs; - return true; - case OP_MOD: - *num = static_cast<double>(static_cast<int64_t>(lhs) % - static_cast<int64_t>(rhs)); - return true; - case OP_NEGATE: - *num = -lhs; - return true; - default: - NOTREACHED(); - return false; - } -} - -bool Query::GetAsDouble(const TraceEvent& event, double* num) const { - switch (type_) { - case QUERY_ARITHMETIC_OPERATOR: - return EvaluateArithmeticOperator(event, num); - case QUERY_EVENT_MEMBER: - return GetMemberValueAsDouble(event, num); - case QUERY_NUMBER: - *num = number_; - return true; - default: - return false; - } -} - -bool Query::GetAsString(const TraceEvent& event, std::string* str) const { - switch (type_) { - case QUERY_EVENT_MEMBER: - return GetMemberValueAsString(event, str); - case QUERY_STRING: - *str = string_; - return true; - default: - return false; - } -} - -const TraceEvent* Query::SelectTargetEvent(const TraceEvent* event, - TraceEventMember member) { - if (member >= OTHER_FIRST_MEMBER && member <= OTHER_LAST_MEMBER) { - return event->other_event; - } else if (member >= PREV_FIRST_MEMBER && member <= PREV_LAST_MEMBER) { - return event->prev_event; - } else { - return event; - } -} - -bool Query::GetMemberValueAsDouble(const TraceEvent& event, - double* num) const { - DCHECK_EQ(QUERY_EVENT_MEMBER, type_); - - // This could be a request for a member of |event| or a member of |event|'s - // associated previous or next event. Store the target event in the_event: - const TraceEvent* the_event = SelectTargetEvent(&event, member_); - - // Request for member of associated event, but there is no associated event. - if (!the_event) - return false; - - switch (member_) { - case EVENT_PID: - case OTHER_PID: - case PREV_PID: - *num = static_cast<double>(the_event->thread.process_id); - return true; - case EVENT_TID: - case OTHER_TID: - case PREV_TID: - *num = static_cast<double>(the_event->thread.thread_id); - return true; - case EVENT_TIME: - case OTHER_TIME: - case PREV_TIME: - *num = the_event->timestamp; - return true; - case EVENT_DURATION: - if (!the_event->has_other_event()) - return false; - *num = the_event->GetAbsTimeToOtherEvent(); - return true; - case EVENT_COMPLETE_DURATION: - if (the_event->phase != TRACE_EVENT_PHASE_COMPLETE) - return false; - *num = the_event->duration; - return true; - case EVENT_PHASE: - case OTHER_PHASE: - case PREV_PHASE: - *num = static_cast<double>(the_event->phase); - return true; - case EVENT_HAS_STRING_ARG: - case OTHER_HAS_STRING_ARG: - case PREV_HAS_STRING_ARG: - *num = (the_event->HasStringArg(string_) ? 1.0 : 0.0); - return true; - case EVENT_HAS_NUMBER_ARG: - case OTHER_HAS_NUMBER_ARG: - case PREV_HAS_NUMBER_ARG: - *num = (the_event->HasNumberArg(string_) ? 1.0 : 0.0); - return true; - case EVENT_ARG: - case OTHER_ARG: - case PREV_ARG: { - // Search for the argument name and return its value if found. - std::map<std::string, double>::const_iterator num_i = - the_event->arg_numbers.find(string_); - if (num_i == the_event->arg_numbers.end()) - return false; - *num = num_i->second; - return true; - } - case EVENT_HAS_OTHER: - // return 1.0 (true) if the other event exists - *num = event.other_event ? 1.0 : 0.0; - return true; - case EVENT_HAS_PREV: - *num = event.prev_event ? 1.0 : 0.0; - return true; - default: - return false; - } -} - -bool Query::GetMemberValueAsString(const TraceEvent& event, - std::string* str) const { - DCHECK_EQ(QUERY_EVENT_MEMBER, type_); - - // This could be a request for a member of |event| or a member of |event|'s - // associated previous or next event. Store the target event in the_event: - const TraceEvent* the_event = SelectTargetEvent(&event, member_); - - // Request for member of associated event, but there is no associated event. - if (!the_event) - return false; - - switch (member_) { - case EVENT_CATEGORY: - case OTHER_CATEGORY: - case PREV_CATEGORY: - *str = the_event->category; - return true; - case EVENT_NAME: - case OTHER_NAME: - case PREV_NAME: - *str = the_event->name; - return true; - case EVENT_ID: - case OTHER_ID: - case PREV_ID: - *str = the_event->id; - return true; - case EVENT_ARG: - case OTHER_ARG: - case PREV_ARG: { - // Search for the argument name and return its value if found. - std::map<std::string, std::string>::const_iterator str_i = - the_event->arg_strings.find(string_); - if (str_i == the_event->arg_strings.end()) - return false; - *str = str_i->second; - return true; - } - default: - return false; - } -} - -Query::Query(const std::string& str) - : type_(QUERY_STRING), - operator_(OP_INVALID), - member_(EVENT_INVALID), - number_(0), - string_(str), - is_pattern_(false) { -} - -Query::Query(double num) - : type_(QUERY_NUMBER), - operator_(OP_INVALID), - member_(EVENT_INVALID), - number_(num), - is_pattern_(false) { -} -const Query& Query::left() const { - return left_->query(); -} - -const Query& Query::right() const { - return right_->query(); -} - -Query Query::operator==(const Query& rhs) const { - return Query(*this, rhs, OP_EQ); -} - -Query Query::operator!=(const Query& rhs) const { - return Query(*this, rhs, OP_NE); -} - -Query Query::operator<(const Query& rhs) const { - return Query(*this, rhs, OP_LT); -} - -Query Query::operator<=(const Query& rhs) const { - return Query(*this, rhs, OP_LE); -} - -Query Query::operator>(const Query& rhs) const { - return Query(*this, rhs, OP_GT); -} - -Query Query::operator>=(const Query& rhs) const { - return Query(*this, rhs, OP_GE); -} - -Query Query::operator&&(const Query& rhs) const { - return Query(*this, rhs, OP_AND); -} - -Query Query::operator||(const Query& rhs) const { - return Query(*this, rhs, OP_OR); -} - -Query Query::operator!() const { - return Query(*this, OP_NOT); -} - -Query Query::operator+(const Query& rhs) const { - return Query(*this, rhs, OP_ADD); -} - -Query Query::operator-(const Query& rhs) const { - return Query(*this, rhs, OP_SUB); -} - -Query Query::operator*(const Query& rhs) const { - return Query(*this, rhs, OP_MUL); -} - -Query Query::operator/(const Query& rhs) const { - return Query(*this, rhs, OP_DIV); -} - -Query Query::operator%(const Query& rhs) const { - return Query(*this, rhs, OP_MOD); -} - -Query Query::operator-() const { - return Query(*this, OP_NEGATE); -} - - -Query::Query(const Query& left, const Query& right, Operator binary_op) - : operator_(binary_op), - left_(new QueryNode(left)), - right_(new QueryNode(right)), - member_(EVENT_INVALID), - number_(0) { - type_ = (binary_op < OP_ADD ? - QUERY_BOOLEAN_OPERATOR : QUERY_ARITHMETIC_OPERATOR); -} - -Query::Query(const Query& left, Operator unary_op) - : operator_(unary_op), - left_(new QueryNode(left)), - member_(EVENT_INVALID), - number_(0) { - type_ = (unary_op < OP_ADD ? - QUERY_BOOLEAN_OPERATOR : QUERY_ARITHMETIC_OPERATOR); -} - -namespace { - -// Search |events| for |query| and add matches to |output|. -size_t FindMatchingEvents(const std::vector<TraceEvent>& events, - const Query& query, - TraceEventVector* output, - bool ignore_metadata_events) { - for (size_t i = 0; i < events.size(); ++i) { - if (ignore_metadata_events && events[i].phase == TRACE_EVENT_PHASE_METADATA) - continue; - if (query.Evaluate(events[i])) - output->push_back(&events[i]); - } - return output->size(); -} - -bool ParseEventsFromJson(const std::string& json, - std::vector<TraceEvent>* output) { - std::unique_ptr<base::Value> root = base::JSONReader::Read(json); - - base::ListValue* root_list = NULL; - if (!root.get() || !root->GetAsList(&root_list)) - return false; - - for (size_t i = 0; i < root_list->GetSize(); ++i) { - base::Value* item = NULL; - if (root_list->Get(i, &item)) { - TraceEvent event; - if (event.SetFromJSON(item)) - output->push_back(std::move(event)); - else - return false; - } - } - - return true; -} - -} // namespace - -// TraceAnalyzer - -TraceAnalyzer::TraceAnalyzer() - : ignore_metadata_events_(false), - allow_assocation_changes_(true) {} - -TraceAnalyzer::~TraceAnalyzer() { -} - -// static -TraceAnalyzer* TraceAnalyzer::Create(const std::string& json_events) { - std::unique_ptr<TraceAnalyzer> analyzer(new TraceAnalyzer()); - if (analyzer->SetEvents(json_events)) - return analyzer.release(); - return NULL; -} - -bool TraceAnalyzer::SetEvents(const std::string& json_events) { - raw_events_.clear(); - if (!ParseEventsFromJson(json_events, &raw_events_)) - return false; - std::stable_sort(raw_events_.begin(), raw_events_.end()); - ParseMetadata(); - return true; -} - -void TraceAnalyzer::AssociateBeginEndEvents() { - using trace_analyzer::Query; - - Query begin(Query::EventPhaseIs(TRACE_EVENT_PHASE_BEGIN)); - Query end(Query::EventPhaseIs(TRACE_EVENT_PHASE_END)); - Query match(Query::EventName() == Query::OtherName() && - Query::EventCategory() == Query::OtherCategory() && - Query::EventTid() == Query::OtherTid() && - Query::EventPid() == Query::OtherPid()); - - AssociateEvents(begin, end, match); -} - -void TraceAnalyzer::AssociateAsyncBeginEndEvents(bool match_pid) { - using trace_analyzer::Query; - - Query begin( - Query::EventPhaseIs(TRACE_EVENT_PHASE_ASYNC_BEGIN) || - Query::EventPhaseIs(TRACE_EVENT_PHASE_ASYNC_STEP_INTO) || - Query::EventPhaseIs(TRACE_EVENT_PHASE_ASYNC_STEP_PAST)); - Query end(Query::EventPhaseIs(TRACE_EVENT_PHASE_ASYNC_END) || - Query::EventPhaseIs(TRACE_EVENT_PHASE_ASYNC_STEP_INTO) || - Query::EventPhaseIs(TRACE_EVENT_PHASE_ASYNC_STEP_PAST)); - Query match(Query::EventCategory() == Query::OtherCategory() && - Query::EventId() == Query::OtherId()); - - if (match_pid) { - match = match && Query::EventPid() == Query::OtherPid(); - } - - AssociateEvents(begin, end, match); -} - -void TraceAnalyzer::AssociateEvents(const Query& first, - const Query& second, - const Query& match) { - DCHECK(allow_assocation_changes_) - << "AssociateEvents not allowed after FindEvents"; - - // Search for matching begin/end event pairs. When a matching end is found, - // it is associated with the begin event. - std::vector<TraceEvent*> begin_stack; - for (size_t event_index = 0; event_index < raw_events_.size(); - ++event_index) { - - TraceEvent& this_event = raw_events_[event_index]; - - if (second.Evaluate(this_event)) { - // Search stack for matching begin, starting from end. - for (int stack_index = static_cast<int>(begin_stack.size()) - 1; - stack_index >= 0; --stack_index) { - TraceEvent& begin_event = *begin_stack[stack_index]; - - // Temporarily set other to test against the match query. - const TraceEvent* other_backup = begin_event.other_event; - begin_event.other_event = &this_event; - if (match.Evaluate(begin_event)) { - // Found a matching begin/end pair. - // Set the associated previous event - this_event.prev_event = &begin_event; - // Erase the matching begin event index from the stack. - begin_stack.erase(begin_stack.begin() + stack_index); - break; - } - - // Not a match, restore original other and continue. - begin_event.other_event = other_backup; - } - } - // Even if this_event is a |second| event that has matched an earlier - // |first| event, it can still also be a |first| event and be associated - // with a later |second| event. - if (first.Evaluate(this_event)) { - begin_stack.push_back(&this_event); - } - } -} - -void TraceAnalyzer::MergeAssociatedEventArgs() { - for (size_t i = 0; i < raw_events_.size(); ++i) { - // Merge all associated events with the first event. - const TraceEvent* other = raw_events_[i].other_event; - // Avoid looping by keeping set of encountered TraceEvents. - std::set<const TraceEvent*> encounters; - encounters.insert(&raw_events_[i]); - while (other && encounters.find(other) == encounters.end()) { - encounters.insert(other); - raw_events_[i].arg_numbers.insert( - other->arg_numbers.begin(), - other->arg_numbers.end()); - raw_events_[i].arg_strings.insert( - other->arg_strings.begin(), - other->arg_strings.end()); - other = other->other_event; - } - } -} - -size_t TraceAnalyzer::FindEvents(const Query& query, TraceEventVector* output) { - allow_assocation_changes_ = false; - output->clear(); - return FindMatchingEvents( - raw_events_, query, output, ignore_metadata_events_); -} - -const TraceEvent* TraceAnalyzer::FindFirstOf(const Query& query) { - TraceEventVector output; - if (FindEvents(query, &output) > 0) - return output.front(); - return NULL; -} - -const TraceEvent* TraceAnalyzer::FindLastOf(const Query& query) { - TraceEventVector output; - if (FindEvents(query, &output) > 0) - return output.back(); - return NULL; -} - -const std::string& TraceAnalyzer::GetThreadName( - const TraceEvent::ProcessThreadID& thread) { - // If thread is not found, just add and return empty string. - return thread_names_[thread]; -} - -void TraceAnalyzer::ParseMetadata() { - for (size_t i = 0; i < raw_events_.size(); ++i) { - TraceEvent& this_event = raw_events_[i]; - // Check for thread name metadata. - if (this_event.phase != TRACE_EVENT_PHASE_METADATA || - this_event.name != "thread_name") - continue; - std::map<std::string, std::string>::const_iterator string_it = - this_event.arg_strings.find("name"); - if (string_it != this_event.arg_strings.end()) - thread_names_[this_event.thread] = string_it->second; - } -} - -// TraceEventVector utility functions. - -bool GetRateStats(const TraceEventVector& events, - RateStats* stats, - const RateStatsOptions* options) { - DCHECK(stats); - // Need at least 3 events to calculate rate stats. - const size_t kMinEvents = 3; - if (events.size() < kMinEvents) { - LOG(ERROR) << "Not enough events: " << events.size(); - return false; - } - - std::vector<double> deltas; - size_t num_deltas = events.size() - 1; - for (size_t i = 0; i < num_deltas; ++i) { - double delta = events.at(i + 1)->timestamp - events.at(i)->timestamp; - if (delta < 0.0) { - LOG(ERROR) << "Events are out of order"; - return false; - } - deltas.push_back(delta); - } - - std::sort(deltas.begin(), deltas.end()); - - if (options) { - if (options->trim_min + options->trim_max > events.size() - kMinEvents) { - LOG(ERROR) << "Attempt to trim too many events"; - return false; - } - deltas.erase(deltas.begin(), deltas.begin() + options->trim_min); - deltas.erase(deltas.end() - options->trim_max, deltas.end()); - } - - num_deltas = deltas.size(); - double delta_sum = 0.0; - for (size_t i = 0; i < num_deltas; ++i) - delta_sum += deltas[i]; - - stats->min_us = *std::min_element(deltas.begin(), deltas.end()); - stats->max_us = *std::max_element(deltas.begin(), deltas.end()); - stats->mean_us = delta_sum / static_cast<double>(num_deltas); - - double sum_mean_offsets_squared = 0.0; - for (size_t i = 0; i < num_deltas; ++i) { - double offset = fabs(deltas[i] - stats->mean_us); - sum_mean_offsets_squared += offset * offset; - } - stats->standard_deviation_us = - sqrt(sum_mean_offsets_squared / static_cast<double>(num_deltas - 1)); - - return true; -} - -bool FindFirstOf(const TraceEventVector& events, - const Query& query, - size_t position, - size_t* return_index) { - DCHECK(return_index); - for (size_t i = position; i < events.size(); ++i) { - if (query.Evaluate(*events[i])) { - *return_index = i; - return true; - } - } - return false; -} - -bool FindLastOf(const TraceEventVector& events, - const Query& query, - size_t position, - size_t* return_index) { - DCHECK(return_index); - for (size_t i = std::min(position + 1, events.size()); i != 0; --i) { - if (query.Evaluate(*events[i - 1])) { - *return_index = i - 1; - return true; - } - } - return false; -} - -bool FindClosest(const TraceEventVector& events, - const Query& query, - size_t position, - size_t* return_closest, - size_t* return_second_closest) { - DCHECK(return_closest); - if (events.empty() || position >= events.size()) - return false; - size_t closest = events.size(); - size_t second_closest = events.size(); - for (size_t i = 0; i < events.size(); ++i) { - if (!query.Evaluate(*events.at(i))) - continue; - if (closest == events.size()) { - closest = i; - continue; - } - if (fabs(events.at(i)->timestamp - events.at(position)->timestamp) < - fabs(events.at(closest)->timestamp - events.at(position)->timestamp)) { - second_closest = closest; - closest = i; - } else if (second_closest == events.size()) { - second_closest = i; - } - } - - if (closest < events.size() && - (!return_second_closest || second_closest < events.size())) { - *return_closest = closest; - if (return_second_closest) - *return_second_closest = second_closest; - return true; - } - - return false; -} - -size_t CountMatches(const TraceEventVector& events, - const Query& query, - size_t begin_position, - size_t end_position) { - if (begin_position >= events.size()) - return 0u; - end_position = (end_position < events.size()) ? end_position : events.size(); - size_t count = 0u; - for (size_t i = begin_position; i < end_position; ++i) { - if (query.Evaluate(*events.at(i))) - ++count; - } - return count; -} - -} // namespace trace_analyzer diff --git a/base/test/trace_event_analyzer.h b/base/test/trace_event_analyzer.h deleted file mode 100644 index e43a525644..0000000000 --- a/base/test/trace_event_analyzer.h +++ /dev/null @@ -1,810 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Use trace_analyzer::Query and trace_analyzer::TraceAnalyzer to search for -// specific trace events that were generated by the trace_event.h API. -// -// Basic procedure: -// - Get trace events JSON string from base::trace_event::TraceLog. -// - Create TraceAnalyzer with JSON string. -// - Call TraceAnalyzer::AssociateBeginEndEvents (optional). -// - Call TraceAnalyzer::AssociateEvents (zero or more times). -// - Call TraceAnalyzer::FindEvents with queries to find specific events. -// -// A Query is a boolean expression tree that evaluates to true or false for a -// given trace event. Queries can be combined into a tree using boolean, -// arithmetic and comparison operators that refer to data of an individual trace -// event. -// -// The events are returned as trace_analyzer::TraceEvent objects. -// TraceEvent contains a single trace event's data, as well as a pointer to -// a related trace event. The related trace event is typically the matching end -// of a begin event or the matching begin of an end event. -// -// The following examples use this basic setup code to construct TraceAnalyzer -// with the json trace string retrieved from TraceLog and construct an event -// vector for retrieving events: -// -// TraceAnalyzer analyzer(json_events); -// TraceEventVector events; -// -// EXAMPLE 1: Find events named "my_event". -// -// analyzer.FindEvents(Query(EVENT_NAME) == "my_event", &events); -// -// EXAMPLE 2: Find begin events named "my_event" with duration > 1 second. -// -// Query q = (Query(EVENT_NAME) == Query::String("my_event") && -// Query(EVENT_PHASE) == Query::Phase(TRACE_EVENT_PHASE_BEGIN) && -// Query(EVENT_DURATION) > Query::Double(1000000.0)); -// analyzer.FindEvents(q, &events); -// -// EXAMPLE 3: Associating event pairs across threads. -// -// If the test needs to analyze something that starts and ends on different -// threads, the test needs to use INSTANT events. The typical procedure is to -// specify the same unique ID as a TRACE_EVENT argument on both the start and -// finish INSTANT events. Then use the following procedure to associate those -// events. -// -// Step 1: instrument code with custom begin/end trace events. -// [Thread 1 tracing code] -// TRACE_EVENT_INSTANT1("test_latency", "timing1_begin", "id", 3); -// [Thread 2 tracing code] -// TRACE_EVENT_INSTANT1("test_latency", "timing1_end", "id", 3); -// -// Step 2: associate these custom begin/end pairs. -// Query begin(Query(EVENT_NAME) == Query::String("timing1_begin")); -// Query end(Query(EVENT_NAME) == Query::String("timing1_end")); -// Query match(Query(EVENT_ARG, "id") == Query(OTHER_ARG, "id")); -// analyzer.AssociateEvents(begin, end, match); -// -// Step 3: search for "timing1_begin" events with existing other event. -// Query q = (Query(EVENT_NAME) == Query::String("timing1_begin") && -// Query(EVENT_HAS_OTHER)); -// analyzer.FindEvents(q, &events); -// -// Step 4: analyze events, such as checking durations. -// for (size_t i = 0; i < events.size(); ++i) { -// double duration; -// EXPECT_TRUE(events[i].GetAbsTimeToOtherEvent(&duration)); -// EXPECT_LT(duration, 1000000.0/60.0); // expect less than 1/60 second. -// } - - -#ifndef BASE_TEST_TRACE_EVENT_ANALYZER_H_ -#define BASE_TEST_TRACE_EVENT_ANALYZER_H_ - -#include <stddef.h> -#include <stdint.h> - -#include <map> - -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "base/trace_event/trace_event.h" - -namespace base { -class Value; -} - -namespace trace_analyzer { -class QueryNode; - -// trace_analyzer::TraceEvent is a more convenient form of the -// base::trace_event::TraceEvent class to make tracing-based tests easier to -// write. -struct TraceEvent { - // ProcessThreadID contains a Process ID and Thread ID. - struct ProcessThreadID { - ProcessThreadID() : process_id(0), thread_id(0) {} - ProcessThreadID(int process_id, int thread_id) - : process_id(process_id), thread_id(thread_id) {} - bool operator< (const ProcessThreadID& rhs) const { - if (process_id != rhs.process_id) - return process_id < rhs.process_id; - return thread_id < rhs.thread_id; - } - int process_id; - int thread_id; - }; - - TraceEvent(); - TraceEvent(TraceEvent&& other); - ~TraceEvent(); - - bool SetFromJSON(const base::Value* event_value) WARN_UNUSED_RESULT; - - bool operator< (const TraceEvent& rhs) const { - return timestamp < rhs.timestamp; - } - - TraceEvent& operator=(TraceEvent&& rhs); - - bool has_other_event() const { return other_event; } - - // Returns absolute duration in microseconds between this event and other - // event. Must have already verified that other_event exists by - // Query(EVENT_HAS_OTHER) or by calling has_other_event(). - double GetAbsTimeToOtherEvent() const; - - // Return the argument value if it exists and it is a string. - bool GetArgAsString(const std::string& name, std::string* arg) const; - // Return the argument value if it exists and it is a number. - bool GetArgAsNumber(const std::string& name, double* arg) const; - // Return the argument value if it exists. - bool GetArgAsValue(const std::string& name, - std::unique_ptr<base::Value>* arg) const; - - // Check if argument exists and is string. - bool HasStringArg(const std::string& name) const; - // Check if argument exists and is number (double, int or bool). - bool HasNumberArg(const std::string& name) const; - // Check if argument exists. - bool HasArg(const std::string& name) const; - - // Get known existing arguments as specific types. - // Useful when you have already queried the argument with - // Query(HAS_NUMBER_ARG) or Query(HAS_STRING_ARG). - std::string GetKnownArgAsString(const std::string& name) const; - double GetKnownArgAsDouble(const std::string& name) const; - int GetKnownArgAsInt(const std::string& name) const; - bool GetKnownArgAsBool(const std::string& name) const; - std::unique_ptr<base::Value> GetKnownArgAsValue( - const std::string& name) const; - - // Process ID and Thread ID. - ProcessThreadID thread; - - // Time since epoch in microseconds. - // Stored as double to match its JSON representation. - double timestamp; - double duration; - char phase; - std::string category; - std::string name; - std::string id; - - // All numbers and bool values from TraceEvent args are cast to double. - // bool becomes 1.0 (true) or 0.0 (false). - std::map<std::string, double> arg_numbers; - std::map<std::string, std::string> arg_strings; - std::map<std::string, std::unique_ptr<base::Value>> arg_values; - - // The other event associated with this event (or NULL). - const TraceEvent* other_event; - - // A back-link for |other_event|. That is, if other_event is not null, then - // |event->other_event->prev_event == event| is always true. - const TraceEvent* prev_event; -}; - -typedef std::vector<const TraceEvent*> TraceEventVector; - -class Query { - public: - Query(const Query& query); - - ~Query(); - - //////////////////////////////////////////////////////////////// - // Query literal values - - // Compare with the given string. - static Query String(const std::string& str); - - // Compare with the given number. - static Query Double(double num); - static Query Int(int32_t num); - static Query Uint(uint32_t num); - - // Compare with the given bool. - static Query Bool(bool boolean); - - // Compare with the given phase. - static Query Phase(char phase); - - // Compare with the given string pattern. Only works with == and != operators. - // Example: Query(EVENT_NAME) == Query::Pattern("MyEvent*") - static Query Pattern(const std::string& pattern); - - //////////////////////////////////////////////////////////////// - // Query event members - - static Query EventPid() { return Query(EVENT_PID); } - - static Query EventTid() { return Query(EVENT_TID); } - - // Return the timestamp of the event in microseconds since epoch. - static Query EventTime() { return Query(EVENT_TIME); } - - // Return the absolute time between event and other event in microseconds. - // Only works if Query::EventHasOther() == true. - static Query EventDuration() { return Query(EVENT_DURATION); } - - // Return the duration of a COMPLETE event. - static Query EventCompleteDuration() { - return Query(EVENT_COMPLETE_DURATION); - } - - static Query EventPhase() { return Query(EVENT_PHASE); } - - static Query EventCategory() { return Query(EVENT_CATEGORY); } - - static Query EventName() { return Query(EVENT_NAME); } - - static Query EventId() { return Query(EVENT_ID); } - - static Query EventPidIs(int process_id) { - return Query(EVENT_PID) == Query::Int(process_id); - } - - static Query EventTidIs(int thread_id) { - return Query(EVENT_TID) == Query::Int(thread_id); - } - - static Query EventThreadIs(const TraceEvent::ProcessThreadID& thread) { - return EventPidIs(thread.process_id) && EventTidIs(thread.thread_id); - } - - static Query EventTimeIs(double timestamp) { - return Query(EVENT_TIME) == Query::Double(timestamp); - } - - static Query EventDurationIs(double duration) { - return Query(EVENT_DURATION) == Query::Double(duration); - } - - static Query EventPhaseIs(char phase) { - return Query(EVENT_PHASE) == Query::Phase(phase); - } - - static Query EventCategoryIs(const std::string& category) { - return Query(EVENT_CATEGORY) == Query::String(category); - } - - static Query EventNameIs(const std::string& name) { - return Query(EVENT_NAME) == Query::String(name); - } - - static Query EventIdIs(const std::string& id) { - return Query(EVENT_ID) == Query::String(id); - } - - // Evaluates to true if arg exists and is a string. - static Query EventHasStringArg(const std::string& arg_name) { - return Query(EVENT_HAS_STRING_ARG, arg_name); - } - - // Evaluates to true if arg exists and is a number. - // Number arguments include types double, int and bool. - static Query EventHasNumberArg(const std::string& arg_name) { - return Query(EVENT_HAS_NUMBER_ARG, arg_name); - } - - // Evaluates to arg value (string or number). - static Query EventArg(const std::string& arg_name) { - return Query(EVENT_ARG, arg_name); - } - - // Return true if associated event exists. - static Query EventHasOther() { return Query(EVENT_HAS_OTHER); } - - // Access the associated other_event's members: - - static Query OtherPid() { return Query(OTHER_PID); } - - static Query OtherTid() { return Query(OTHER_TID); } - - static Query OtherTime() { return Query(OTHER_TIME); } - - static Query OtherPhase() { return Query(OTHER_PHASE); } - - static Query OtherCategory() { return Query(OTHER_CATEGORY); } - - static Query OtherName() { return Query(OTHER_NAME); } - - static Query OtherId() { return Query(OTHER_ID); } - - static Query OtherPidIs(int process_id) { - return Query(OTHER_PID) == Query::Int(process_id); - } - - static Query OtherTidIs(int thread_id) { - return Query(OTHER_TID) == Query::Int(thread_id); - } - - static Query OtherThreadIs(const TraceEvent::ProcessThreadID& thread) { - return OtherPidIs(thread.process_id) && OtherTidIs(thread.thread_id); - } - - static Query OtherTimeIs(double timestamp) { - return Query(OTHER_TIME) == Query::Double(timestamp); - } - - static Query OtherPhaseIs(char phase) { - return Query(OTHER_PHASE) == Query::Phase(phase); - } - - static Query OtherCategoryIs(const std::string& category) { - return Query(OTHER_CATEGORY) == Query::String(category); - } - - static Query OtherNameIs(const std::string& name) { - return Query(OTHER_NAME) == Query::String(name); - } - - static Query OtherIdIs(const std::string& id) { - return Query(OTHER_ID) == Query::String(id); - } - - // Evaluates to true if arg exists and is a string. - static Query OtherHasStringArg(const std::string& arg_name) { - return Query(OTHER_HAS_STRING_ARG, arg_name); - } - - // Evaluates to true if arg exists and is a number. - // Number arguments include types double, int and bool. - static Query OtherHasNumberArg(const std::string& arg_name) { - return Query(OTHER_HAS_NUMBER_ARG, arg_name); - } - - // Evaluates to arg value (string or number). - static Query OtherArg(const std::string& arg_name) { - return Query(OTHER_ARG, arg_name); - } - - // Access the associated prev_event's members: - - static Query PrevPid() { return Query(PREV_PID); } - - static Query PrevTid() { return Query(PREV_TID); } - - static Query PrevTime() { return Query(PREV_TIME); } - - static Query PrevPhase() { return Query(PREV_PHASE); } - - static Query PrevCategory() { return Query(PREV_CATEGORY); } - - static Query PrevName() { return Query(PREV_NAME); } - - static Query PrevId() { return Query(PREV_ID); } - - static Query PrevPidIs(int process_id) { - return Query(PREV_PID) == Query::Int(process_id); - } - - static Query PrevTidIs(int thread_id) { - return Query(PREV_TID) == Query::Int(thread_id); - } - - static Query PrevThreadIs(const TraceEvent::ProcessThreadID& thread) { - return PrevPidIs(thread.process_id) && PrevTidIs(thread.thread_id); - } - - static Query PrevTimeIs(double timestamp) { - return Query(PREV_TIME) == Query::Double(timestamp); - } - - static Query PrevPhaseIs(char phase) { - return Query(PREV_PHASE) == Query::Phase(phase); - } - - static Query PrevCategoryIs(const std::string& category) { - return Query(PREV_CATEGORY) == Query::String(category); - } - - static Query PrevNameIs(const std::string& name) { - return Query(PREV_NAME) == Query::String(name); - } - - static Query PrevIdIs(const std::string& id) { - return Query(PREV_ID) == Query::String(id); - } - - // Evaluates to true if arg exists and is a string. - static Query PrevHasStringArg(const std::string& arg_name) { - return Query(PREV_HAS_STRING_ARG, arg_name); - } - - // Evaluates to true if arg exists and is a number. - // Number arguments include types double, int and bool. - static Query PrevHasNumberArg(const std::string& arg_name) { - return Query(PREV_HAS_NUMBER_ARG, arg_name); - } - - // Evaluates to arg value (string or number). - static Query PrevArg(const std::string& arg_name) { - return Query(PREV_ARG, arg_name); - } - - //////////////////////////////////////////////////////////////// - // Common queries: - - // Find BEGIN events that have a corresponding END event. - static Query MatchBeginWithEnd() { - return (Query(EVENT_PHASE) == Query::Phase(TRACE_EVENT_PHASE_BEGIN)) && - Query(EVENT_HAS_OTHER); - } - - // Find COMPLETE events. - static Query MatchComplete() { - return (Query(EVENT_PHASE) == Query::Phase(TRACE_EVENT_PHASE_COMPLETE)); - } - - // Find ASYNC_BEGIN events that have a corresponding ASYNC_END event. - static Query MatchAsyncBeginWithNext() { - return (Query(EVENT_PHASE) == - Query::Phase(TRACE_EVENT_PHASE_ASYNC_BEGIN)) && - Query(EVENT_HAS_OTHER); - } - - // Find BEGIN events of given |name| which also have associated END events. - static Query MatchBeginName(const std::string& name) { - return (Query(EVENT_NAME) == Query(name)) && MatchBeginWithEnd(); - } - - // Find COMPLETE events of given |name|. - static Query MatchCompleteName(const std::string& name) { - return (Query(EVENT_NAME) == Query(name)) && MatchComplete(); - } - - // Match given Process ID and Thread ID. - static Query MatchThread(const TraceEvent::ProcessThreadID& thread) { - return (Query(EVENT_PID) == Query::Int(thread.process_id)) && - (Query(EVENT_TID) == Query::Int(thread.thread_id)); - } - - // Match event pair that spans multiple threads. - static Query MatchCrossThread() { - return (Query(EVENT_PID) != Query(OTHER_PID)) || - (Query(EVENT_TID) != Query(OTHER_TID)); - } - - //////////////////////////////////////////////////////////////// - // Operators: - - // Boolean operators: - Query operator==(const Query& rhs) const; - Query operator!=(const Query& rhs) const; - Query operator< (const Query& rhs) const; - Query operator<=(const Query& rhs) const; - Query operator> (const Query& rhs) const; - Query operator>=(const Query& rhs) const; - Query operator&&(const Query& rhs) const; - Query operator||(const Query& rhs) const; - Query operator!() const; - - // Arithmetic operators: - // Following operators are applied to double arguments: - Query operator+(const Query& rhs) const; - Query operator-(const Query& rhs) const; - Query operator*(const Query& rhs) const; - Query operator/(const Query& rhs) const; - Query operator-() const; - // Mod operates on int64_t args (doubles are casted to int64_t beforehand): - Query operator%(const Query& rhs) const; - - // Return true if the given event matches this query tree. - // This is a recursive method that walks the query tree. - bool Evaluate(const TraceEvent& event) const; - - private: - enum TraceEventMember { - EVENT_INVALID, - EVENT_PID, - EVENT_TID, - EVENT_TIME, - EVENT_DURATION, - EVENT_COMPLETE_DURATION, - EVENT_PHASE, - EVENT_CATEGORY, - EVENT_NAME, - EVENT_ID, - EVENT_HAS_STRING_ARG, - EVENT_HAS_NUMBER_ARG, - EVENT_ARG, - EVENT_HAS_OTHER, - EVENT_HAS_PREV, - - OTHER_PID, - OTHER_TID, - OTHER_TIME, - OTHER_PHASE, - OTHER_CATEGORY, - OTHER_NAME, - OTHER_ID, - OTHER_HAS_STRING_ARG, - OTHER_HAS_NUMBER_ARG, - OTHER_ARG, - - PREV_PID, - PREV_TID, - PREV_TIME, - PREV_PHASE, - PREV_CATEGORY, - PREV_NAME, - PREV_ID, - PREV_HAS_STRING_ARG, - PREV_HAS_NUMBER_ARG, - PREV_ARG, - - OTHER_FIRST_MEMBER = OTHER_PID, - OTHER_LAST_MEMBER = OTHER_ARG, - - PREV_FIRST_MEMBER = PREV_PID, - PREV_LAST_MEMBER = PREV_ARG, - }; - - enum Operator { - OP_INVALID, - // Boolean operators: - OP_EQ, - OP_NE, - OP_LT, - OP_LE, - OP_GT, - OP_GE, - OP_AND, - OP_OR, - OP_NOT, - // Arithmetic operators: - OP_ADD, - OP_SUB, - OP_MUL, - OP_DIV, - OP_MOD, - OP_NEGATE - }; - - enum QueryType { - QUERY_BOOLEAN_OPERATOR, - QUERY_ARITHMETIC_OPERATOR, - QUERY_EVENT_MEMBER, - QUERY_NUMBER, - QUERY_STRING - }; - - // Compare with the given member. - explicit Query(TraceEventMember member); - - // Compare with the given member argument value. - Query(TraceEventMember member, const std::string& arg_name); - - // Compare with the given string. - explicit Query(const std::string& str); - - // Compare with the given number. - explicit Query(double num); - - // Construct a boolean Query that returns (left <binary_op> right). - Query(const Query& left, const Query& right, Operator binary_op); - - // Construct a boolean Query that returns (<binary_op> left). - Query(const Query& left, Operator unary_op); - - // Try to compare left_ against right_ based on operator_. - // If either left or right does not convert to double, false is returned. - // Otherwise, true is returned and |result| is set to the comparison result. - bool CompareAsDouble(const TraceEvent& event, bool* result) const; - - // Try to compare left_ against right_ based on operator_. - // If either left or right does not convert to string, false is returned. - // Otherwise, true is returned and |result| is set to the comparison result. - bool CompareAsString(const TraceEvent& event, bool* result) const; - - // Attempt to convert this Query to a double. On success, true is returned - // and the double value is stored in |num|. - bool GetAsDouble(const TraceEvent& event, double* num) const; - - // Attempt to convert this Query to a string. On success, true is returned - // and the string value is stored in |str|. - bool GetAsString(const TraceEvent& event, std::string* str) const; - - // Evaluate this Query as an arithmetic operator on left_ and right_. - bool EvaluateArithmeticOperator(const TraceEvent& event, - double* num) const; - - // For QUERY_EVENT_MEMBER Query: attempt to get the double value of the Query. - bool GetMemberValueAsDouble(const TraceEvent& event, double* num) const; - - // For QUERY_EVENT_MEMBER Query: attempt to get the string value of the Query. - bool GetMemberValueAsString(const TraceEvent& event, std::string* num) const; - - // Does this Query represent a value? - bool is_value() const { return type_ != QUERY_BOOLEAN_OPERATOR; } - - bool is_unary_operator() const { - return operator_ == OP_NOT || operator_ == OP_NEGATE; - } - - bool is_comparison_operator() const { - return operator_ != OP_INVALID && operator_ < OP_AND; - } - - static const TraceEvent* SelectTargetEvent(const TraceEvent* ev, - TraceEventMember member); - - const Query& left() const; - const Query& right() const; - - QueryType type_; - Operator operator_; - scoped_refptr<QueryNode> left_; - scoped_refptr<QueryNode> right_; - TraceEventMember member_; - double number_; - std::string string_; - bool is_pattern_; -}; - -// Implementation detail: -// QueryNode allows Query to store a ref-counted query tree. -class QueryNode : public base::RefCounted<QueryNode> { - public: - explicit QueryNode(const Query& query); - const Query& query() const { return query_; } - - private: - friend class base::RefCounted<QueryNode>; - ~QueryNode(); - - Query query_; -}; - -// TraceAnalyzer helps tests search for trace events. -class TraceAnalyzer { - public: - ~TraceAnalyzer(); - - // Use trace events from JSON string generated by tracing API. - // Returns non-NULL if the JSON is successfully parsed. - static TraceAnalyzer* Create(const std::string& json_events) - WARN_UNUSED_RESULT; - - void SetIgnoreMetadataEvents(bool ignore) { - ignore_metadata_events_ = ignore; - } - - // Associate BEGIN and END events with each other. This allows Query(OTHER_*) - // to access the associated event and enables Query(EVENT_DURATION). - // An end event will match the most recent begin event with the same name, - // category, process ID and thread ID. This matches what is shown in - // about:tracing. After association, the BEGIN event will point to the - // matching END event, but the END event will not point to the BEGIN event. - void AssociateBeginEndEvents(); - - // Associate ASYNC_BEGIN, ASYNC_STEP and ASYNC_END events with each other. - // An ASYNC_END event will match the most recent ASYNC_BEGIN or ASYNC_STEP - // event with the same name, category, and ID. This creates a singly linked - // list of ASYNC_BEGIN->ASYNC_STEP...->ASYNC_END. - // |match_pid| - If true, will only match async events which are running - // under the same process ID, otherwise will allow linking - // async events from different processes. - void AssociateAsyncBeginEndEvents(bool match_pid = true); - - // AssociateEvents can be used to customize event associations by setting the - // other_event member of TraceEvent. This should be used to associate two - // INSTANT events. - // - // The assumptions are: - // - |first| events occur before |second| events. - // - the closest matching |second| event is the correct match. - // - // |first| - Eligible |first| events match this query. - // |second| - Eligible |second| events match this query. - // |match| - This query is run on the |first| event. The OTHER_* EventMember - // queries will point to an eligible |second| event. The query - // should evaluate to true if the |first|/|second| pair is a match. - // - // When a match is found, the pair will be associated by having the first - // event's other_event member point to the other. AssociateEvents does not - // clear previous associations, so it is possible to associate multiple pairs - // of events by calling AssociateEvents more than once with different queries. - // - // NOTE: AssociateEvents will overwrite existing other_event associations if - // the queries pass for events that already had a previous association. - // - // After calling any Find* method, it is not allowed to call AssociateEvents - // again. - void AssociateEvents(const Query& first, - const Query& second, - const Query& match); - - // For each event, copy its arguments to the other_event argument map. If - // argument name already exists, it will not be overwritten. - void MergeAssociatedEventArgs(); - - // Find all events that match query and replace output vector. - size_t FindEvents(const Query& query, TraceEventVector* output); - - // Find first event that matches query or NULL if not found. - const TraceEvent* FindFirstOf(const Query& query); - - // Find last event that matches query or NULL if not found. - const TraceEvent* FindLastOf(const Query& query); - - const std::string& GetThreadName(const TraceEvent::ProcessThreadID& thread); - - private: - TraceAnalyzer(); - - bool SetEvents(const std::string& json_events) WARN_UNUSED_RESULT; - - // Read metadata (thread names, etc) from events. - void ParseMetadata(); - - std::map<TraceEvent::ProcessThreadID, std::string> thread_names_; - std::vector<TraceEvent> raw_events_; - bool ignore_metadata_events_; - bool allow_assocation_changes_; - - DISALLOW_COPY_AND_ASSIGN(TraceAnalyzer); -}; - -// Utility functions for TraceEventVector. - -struct RateStats { - double min_us; - double max_us; - double mean_us; - double standard_deviation_us; -}; - -struct RateStatsOptions { - RateStatsOptions() : trim_min(0u), trim_max(0u) {} - // After the times between events are sorted, the number of specified elements - // will be trimmed before calculating the RateStats. This is useful in cases - // where extreme outliers are tolerable and should not skew the overall - // average. - size_t trim_min; // Trim this many minimum times. - size_t trim_max; // Trim this many maximum times. -}; - -// Calculate min/max/mean and standard deviation from the times between -// adjacent events. -bool GetRateStats(const TraceEventVector& events, - RateStats* stats, - const RateStatsOptions* options); - -// Starting from |position|, find the first event that matches |query|. -// Returns true if found, false otherwise. -bool FindFirstOf(const TraceEventVector& events, - const Query& query, - size_t position, - size_t* return_index); - -// Starting from |position|, find the last event that matches |query|. -// Returns true if found, false otherwise. -bool FindLastOf(const TraceEventVector& events, - const Query& query, - size_t position, - size_t* return_index); - -// Find the closest events to |position| in time that match |query|. -// return_second_closest may be NULL. Closeness is determined by comparing -// with the event timestamp. -// Returns true if found, false otherwise. If both return parameters are -// requested, both must be found for a successful result. -bool FindClosest(const TraceEventVector& events, - const Query& query, - size_t position, - size_t* return_closest, - size_t* return_second_closest); - -// Count matches, inclusive of |begin_position|, exclusive of |end_position|. -size_t CountMatches(const TraceEventVector& events, - const Query& query, - size_t begin_position, - size_t end_position); - -// Count all matches. -static inline size_t CountMatches(const TraceEventVector& events, - const Query& query) { - return CountMatches(events, query, 0u, events.size()); -} - -} // namespace trace_analyzer - -#endif // BASE_TEST_TRACE_EVENT_ANALYZER_H_ diff --git a/base/test/trace_event_analyzer_unittest.cc b/base/test/trace_event_analyzer_unittest.cc deleted file mode 100644 index ce7bce22a0..0000000000 --- a/base/test/trace_event_analyzer_unittest.cc +++ /dev/null @@ -1,961 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/test/trace_event_analyzer.h" - -#include <stddef.h> -#include <stdint.h> - -#include "base/bind.h" -#include "base/memory/ptr_util.h" -#include "base/memory/ref_counted_memory.h" -#include "base/synchronization/waitable_event.h" -#include "base/threading/platform_thread.h" -#include "base/trace_event/trace_buffer.h" -#include "base/trace_event/trace_event_argument.h" -#include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace trace_analyzer { - -namespace { - -class TraceEventAnalyzerTest : public testing::Test { - public: - void ManualSetUp(); - void OnTraceDataCollected( - base::WaitableEvent* flush_complete_event, - const scoped_refptr<base::RefCountedString>& json_events_str, - bool has_more_events); - void BeginTracing(); - void EndTracing(); - - base::trace_event::TraceResultBuffer::SimpleOutput output_; - base::trace_event::TraceResultBuffer buffer_; -}; - -void TraceEventAnalyzerTest::ManualSetUp() { - ASSERT_TRUE(base::trace_event::TraceLog::GetInstance()); - buffer_.SetOutputCallback(output_.GetCallback()); - output_.json_output.clear(); -} - -void TraceEventAnalyzerTest::OnTraceDataCollected( - base::WaitableEvent* flush_complete_event, - const scoped_refptr<base::RefCountedString>& json_events_str, - bool has_more_events) { - buffer_.AddFragment(json_events_str->data()); - if (!has_more_events) - flush_complete_event->Signal(); -} - -void TraceEventAnalyzerTest::BeginTracing() { - output_.json_output.clear(); - buffer_.Start(); - base::trace_event::TraceLog::GetInstance()->SetEnabled( - base::trace_event::TraceConfig("*", ""), - base::trace_event::TraceLog::RECORDING_MODE); -} - -void TraceEventAnalyzerTest::EndTracing() { - base::trace_event::TraceLog::GetInstance()->SetDisabled(); - base::WaitableEvent flush_complete_event( - base::WaitableEvent::ResetPolicy::AUTOMATIC, - base::WaitableEvent::InitialState::NOT_SIGNALED); - base::trace_event::TraceLog::GetInstance()->Flush( - base::Bind(&TraceEventAnalyzerTest::OnTraceDataCollected, - base::Unretained(this), - base::Unretained(&flush_complete_event))); - flush_complete_event.Wait(); - buffer_.Finish(); -} - -} // namespace - -TEST_F(TraceEventAnalyzerTest, NoEvents) { - ManualSetUp(); - - // Create an empty JSON event string: - buffer_.Start(); - buffer_.Finish(); - - std::unique_ptr<TraceAnalyzer> analyzer( - TraceAnalyzer::Create(output_.json_output)); - ASSERT_TRUE(analyzer.get()); - - // Search for all events and verify that nothing is returned. - TraceEventVector found; - analyzer->FindEvents(Query::Bool(true), &found); - EXPECT_EQ(0u, found.size()); -} - -TEST_F(TraceEventAnalyzerTest, TraceEvent) { - ManualSetUp(); - - int int_num = 2; - double double_num = 3.5; - const char str[] = "the string"; - - TraceEvent event; - event.arg_numbers["false"] = 0.0; - event.arg_numbers["true"] = 1.0; - event.arg_numbers["int"] = static_cast<double>(int_num); - event.arg_numbers["double"] = double_num; - event.arg_strings["string"] = str; - event.arg_values["dict"] = WrapUnique(new base::DictionaryValue()); - - ASSERT_TRUE(event.HasNumberArg("false")); - ASSERT_TRUE(event.HasNumberArg("true")); - ASSERT_TRUE(event.HasNumberArg("int")); - ASSERT_TRUE(event.HasNumberArg("double")); - ASSERT_TRUE(event.HasStringArg("string")); - ASSERT_FALSE(event.HasNumberArg("notfound")); - ASSERT_FALSE(event.HasStringArg("notfound")); - ASSERT_TRUE(event.HasArg("dict")); - ASSERT_FALSE(event.HasArg("notfound")); - - EXPECT_FALSE(event.GetKnownArgAsBool("false")); - EXPECT_TRUE(event.GetKnownArgAsBool("true")); - EXPECT_EQ(int_num, event.GetKnownArgAsInt("int")); - EXPECT_EQ(double_num, event.GetKnownArgAsDouble("double")); - EXPECT_STREQ(str, event.GetKnownArgAsString("string").c_str()); - - std::unique_ptr<base::Value> arg; - EXPECT_TRUE(event.GetArgAsValue("dict", &arg)); - EXPECT_EQ(base::Value::Type::DICTIONARY, arg->GetType()); -} - -TEST_F(TraceEventAnalyzerTest, QueryEventMember) { - ManualSetUp(); - - TraceEvent event; - event.thread.process_id = 3; - event.thread.thread_id = 4; - event.timestamp = 1.5; - event.phase = TRACE_EVENT_PHASE_BEGIN; - event.category = "category"; - event.name = "name"; - event.id = "1"; - event.arg_numbers["num"] = 7.0; - event.arg_strings["str"] = "the string"; - - // Other event with all different members: - TraceEvent other; - other.thread.process_id = 5; - other.thread.thread_id = 6; - other.timestamp = 2.5; - other.phase = TRACE_EVENT_PHASE_END; - other.category = "category2"; - other.name = "name2"; - other.id = "2"; - other.arg_numbers["num2"] = 8.0; - other.arg_strings["str2"] = "the string 2"; - - event.other_event = &other; - ASSERT_TRUE(event.has_other_event()); - double duration = event.GetAbsTimeToOtherEvent(); - - Query event_pid = Query::EventPidIs(event.thread.process_id); - Query event_tid = Query::EventTidIs(event.thread.thread_id); - Query event_time = Query::EventTimeIs(event.timestamp); - Query event_duration = Query::EventDurationIs(duration); - Query event_phase = Query::EventPhaseIs(event.phase); - Query event_category = Query::EventCategoryIs(event.category); - Query event_name = Query::EventNameIs(event.name); - Query event_id = Query::EventIdIs(event.id); - Query event_has_arg1 = Query::EventHasNumberArg("num"); - Query event_has_arg2 = Query::EventHasStringArg("str"); - Query event_arg1 = - (Query::EventArg("num") == Query::Double(event.arg_numbers["num"])); - Query event_arg2 = - (Query::EventArg("str") == Query::String(event.arg_strings["str"])); - Query event_has_other = Query::EventHasOther(); - Query other_pid = Query::OtherPidIs(other.thread.process_id); - Query other_tid = Query::OtherTidIs(other.thread.thread_id); - Query other_time = Query::OtherTimeIs(other.timestamp); - Query other_phase = Query::OtherPhaseIs(other.phase); - Query other_category = Query::OtherCategoryIs(other.category); - Query other_name = Query::OtherNameIs(other.name); - Query other_id = Query::OtherIdIs(other.id); - Query other_has_arg1 = Query::OtherHasNumberArg("num2"); - Query other_has_arg2 = Query::OtherHasStringArg("str2"); - Query other_arg1 = - (Query::OtherArg("num2") == Query::Double(other.arg_numbers["num2"])); - Query other_arg2 = - (Query::OtherArg("str2") == Query::String(other.arg_strings["str2"])); - - EXPECT_TRUE(event_pid.Evaluate(event)); - EXPECT_TRUE(event_tid.Evaluate(event)); - EXPECT_TRUE(event_time.Evaluate(event)); - EXPECT_TRUE(event_duration.Evaluate(event)); - EXPECT_TRUE(event_phase.Evaluate(event)); - EXPECT_TRUE(event_category.Evaluate(event)); - EXPECT_TRUE(event_name.Evaluate(event)); - EXPECT_TRUE(event_id.Evaluate(event)); - EXPECT_TRUE(event_has_arg1.Evaluate(event)); - EXPECT_TRUE(event_has_arg2.Evaluate(event)); - EXPECT_TRUE(event_arg1.Evaluate(event)); - EXPECT_TRUE(event_arg2.Evaluate(event)); - EXPECT_TRUE(event_has_other.Evaluate(event)); - EXPECT_TRUE(other_pid.Evaluate(event)); - EXPECT_TRUE(other_tid.Evaluate(event)); - EXPECT_TRUE(other_time.Evaluate(event)); - EXPECT_TRUE(other_phase.Evaluate(event)); - EXPECT_TRUE(other_category.Evaluate(event)); - EXPECT_TRUE(other_name.Evaluate(event)); - EXPECT_TRUE(other_id.Evaluate(event)); - EXPECT_TRUE(other_has_arg1.Evaluate(event)); - EXPECT_TRUE(other_has_arg2.Evaluate(event)); - EXPECT_TRUE(other_arg1.Evaluate(event)); - EXPECT_TRUE(other_arg2.Evaluate(event)); - - // Evaluate event queries against other to verify the queries fail when the - // event members are wrong. - EXPECT_FALSE(event_pid.Evaluate(other)); - EXPECT_FALSE(event_tid.Evaluate(other)); - EXPECT_FALSE(event_time.Evaluate(other)); - EXPECT_FALSE(event_duration.Evaluate(other)); - EXPECT_FALSE(event_phase.Evaluate(other)); - EXPECT_FALSE(event_category.Evaluate(other)); - EXPECT_FALSE(event_name.Evaluate(other)); - EXPECT_FALSE(event_id.Evaluate(other)); - EXPECT_FALSE(event_has_arg1.Evaluate(other)); - EXPECT_FALSE(event_has_arg2.Evaluate(other)); - EXPECT_FALSE(event_arg1.Evaluate(other)); - EXPECT_FALSE(event_arg2.Evaluate(other)); - EXPECT_FALSE(event_has_other.Evaluate(other)); -} - -TEST_F(TraceEventAnalyzerTest, BooleanOperators) { - ManualSetUp(); - - BeginTracing(); - { - TRACE_EVENT_INSTANT1("cat1", "name1", TRACE_EVENT_SCOPE_THREAD, "num", 1); - TRACE_EVENT_INSTANT1("cat1", "name2", TRACE_EVENT_SCOPE_THREAD, "num", 2); - TRACE_EVENT_INSTANT1("cat2", "name3", TRACE_EVENT_SCOPE_THREAD, "num", 3); - TRACE_EVENT_INSTANT1("cat2", "name4", TRACE_EVENT_SCOPE_THREAD, "num", 4); - } - EndTracing(); - - std::unique_ptr<TraceAnalyzer> analyzer( - TraceAnalyzer::Create(output_.json_output)); - ASSERT_TRUE(analyzer); - analyzer->SetIgnoreMetadataEvents(true); - - TraceEventVector found; - - // == - - analyzer->FindEvents(Query::EventCategory() == Query::String("cat1"), &found); - ASSERT_EQ(2u, found.size()); - EXPECT_STREQ("name1", found[0]->name.c_str()); - EXPECT_STREQ("name2", found[1]->name.c_str()); - - analyzer->FindEvents(Query::EventArg("num") == Query::Int(2), &found); - ASSERT_EQ(1u, found.size()); - EXPECT_STREQ("name2", found[0]->name.c_str()); - - // != - - analyzer->FindEvents(Query::EventCategory() != Query::String("cat1"), &found); - ASSERT_EQ(2u, found.size()); - EXPECT_STREQ("name3", found[0]->name.c_str()); - EXPECT_STREQ("name4", found[1]->name.c_str()); - - analyzer->FindEvents(Query::EventArg("num") != Query::Int(2), &found); - ASSERT_EQ(3u, found.size()); - EXPECT_STREQ("name1", found[0]->name.c_str()); - EXPECT_STREQ("name3", found[1]->name.c_str()); - EXPECT_STREQ("name4", found[2]->name.c_str()); - - // < - analyzer->FindEvents(Query::EventArg("num") < Query::Int(2), &found); - ASSERT_EQ(1u, found.size()); - EXPECT_STREQ("name1", found[0]->name.c_str()); - - // <= - analyzer->FindEvents(Query::EventArg("num") <= Query::Int(2), &found); - ASSERT_EQ(2u, found.size()); - EXPECT_STREQ("name1", found[0]->name.c_str()); - EXPECT_STREQ("name2", found[1]->name.c_str()); - - // > - analyzer->FindEvents(Query::EventArg("num") > Query::Int(3), &found); - ASSERT_EQ(1u, found.size()); - EXPECT_STREQ("name4", found[0]->name.c_str()); - - // >= - analyzer->FindEvents(Query::EventArg("num") >= Query::Int(4), &found); - ASSERT_EQ(1u, found.size()); - EXPECT_STREQ("name4", found[0]->name.c_str()); - - // && - analyzer->FindEvents(Query::EventName() != Query::String("name1") && - Query::EventArg("num") < Query::Int(3), &found); - ASSERT_EQ(1u, found.size()); - EXPECT_STREQ("name2", found[0]->name.c_str()); - - // || - analyzer->FindEvents(Query::EventName() == Query::String("name1") || - Query::EventArg("num") == Query::Int(3), &found); - ASSERT_EQ(2u, found.size()); - EXPECT_STREQ("name1", found[0]->name.c_str()); - EXPECT_STREQ("name3", found[1]->name.c_str()); - - // ! - analyzer->FindEvents(!(Query::EventName() == Query::String("name1") || - Query::EventArg("num") == Query::Int(3)), &found); - ASSERT_EQ(2u, found.size()); - EXPECT_STREQ("name2", found[0]->name.c_str()); - EXPECT_STREQ("name4", found[1]->name.c_str()); -} - -TEST_F(TraceEventAnalyzerTest, ArithmeticOperators) { - ManualSetUp(); - - BeginTracing(); - { - // These events are searched for: - TRACE_EVENT_INSTANT2("cat1", "math1", TRACE_EVENT_SCOPE_THREAD, - "a", 10, "b", 5); - TRACE_EVENT_INSTANT2("cat1", "math2", TRACE_EVENT_SCOPE_THREAD, - "a", 10, "b", 10); - // Extra events that never match, for noise: - TRACE_EVENT_INSTANT2("noise", "math3", TRACE_EVENT_SCOPE_THREAD, - "a", 1, "b", 3); - TRACE_EVENT_INSTANT2("noise", "math4", TRACE_EVENT_SCOPE_THREAD, - "c", 10, "d", 5); - } - EndTracing(); - - std::unique_ptr<TraceAnalyzer> analyzer( - TraceAnalyzer::Create(output_.json_output)); - ASSERT_TRUE(analyzer.get()); - - TraceEventVector found; - - // Verify that arithmetic operators function: - - // + - analyzer->FindEvents(Query::EventArg("a") + Query::EventArg("b") == - Query::Int(20), &found); - EXPECT_EQ(1u, found.size()); - EXPECT_STREQ("math2", found.front()->name.c_str()); - - // - - analyzer->FindEvents(Query::EventArg("a") - Query::EventArg("b") == - Query::Int(5), &found); - EXPECT_EQ(1u, found.size()); - EXPECT_STREQ("math1", found.front()->name.c_str()); - - // * - analyzer->FindEvents(Query::EventArg("a") * Query::EventArg("b") == - Query::Int(50), &found); - EXPECT_EQ(1u, found.size()); - EXPECT_STREQ("math1", found.front()->name.c_str()); - - // / - analyzer->FindEvents(Query::EventArg("a") / Query::EventArg("b") == - Query::Int(2), &found); - EXPECT_EQ(1u, found.size()); - EXPECT_STREQ("math1", found.front()->name.c_str()); - - // % - analyzer->FindEvents(Query::EventArg("a") % Query::EventArg("b") == - Query::Int(0), &found); - EXPECT_EQ(2u, found.size()); - - // - (negate) - analyzer->FindEvents(-Query::EventArg("b") == Query::Int(-10), &found); - EXPECT_EQ(1u, found.size()); - EXPECT_STREQ("math2", found.front()->name.c_str()); -} - -TEST_F(TraceEventAnalyzerTest, StringPattern) { - ManualSetUp(); - - BeginTracing(); - { - TRACE_EVENT_INSTANT0("cat1", "name1", TRACE_EVENT_SCOPE_THREAD); - TRACE_EVENT_INSTANT0("cat1", "name2", TRACE_EVENT_SCOPE_THREAD); - TRACE_EVENT_INSTANT0("cat1", "no match", TRACE_EVENT_SCOPE_THREAD); - TRACE_EVENT_INSTANT0("cat1", "name3x", TRACE_EVENT_SCOPE_THREAD); - } - EndTracing(); - - std::unique_ptr<TraceAnalyzer> analyzer( - TraceAnalyzer::Create(output_.json_output)); - ASSERT_TRUE(analyzer.get()); - analyzer->SetIgnoreMetadataEvents(true); - - TraceEventVector found; - - analyzer->FindEvents(Query::EventName() == Query::Pattern("name?"), &found); - ASSERT_EQ(2u, found.size()); - EXPECT_STREQ("name1", found[0]->name.c_str()); - EXPECT_STREQ("name2", found[1]->name.c_str()); - - analyzer->FindEvents(Query::EventName() == Query::Pattern("name*"), &found); - ASSERT_EQ(3u, found.size()); - EXPECT_STREQ("name1", found[0]->name.c_str()); - EXPECT_STREQ("name2", found[1]->name.c_str()); - EXPECT_STREQ("name3x", found[2]->name.c_str()); - - analyzer->FindEvents(Query::EventName() != Query::Pattern("name*"), &found); - ASSERT_EQ(1u, found.size()); - EXPECT_STREQ("no match", found[0]->name.c_str()); -} - -// Test that duration queries work. -TEST_F(TraceEventAnalyzerTest, BeginEndDuration) { - ManualSetUp(); - - const base::TimeDelta kSleepTime = base::TimeDelta::FromMilliseconds(200); - // We will search for events that have a duration of greater than 90% of the - // sleep time, so that there is no flakiness. - int64_t duration_cutoff_us = (kSleepTime.InMicroseconds() * 9) / 10; - - BeginTracing(); - { - TRACE_EVENT_BEGIN0("cat1", "name1"); // found by duration query - TRACE_EVENT_BEGIN0("noise", "name2"); // not searched for, just noise - { - TRACE_EVENT_BEGIN0("cat2", "name3"); // found by duration query - // next event not searched for, just noise - TRACE_EVENT_INSTANT0("noise", "name4", TRACE_EVENT_SCOPE_THREAD); - base::PlatformThread::Sleep(kSleepTime); - TRACE_EVENT_BEGIN0("cat2", "name5"); // not found (duration too short) - TRACE_EVENT_END0("cat2", "name5"); // not found (duration too short) - TRACE_EVENT_END0("cat2", "name3"); // found by duration query - } - TRACE_EVENT_END0("noise", "name2"); // not searched for, just noise - TRACE_EVENT_END0("cat1", "name1"); // found by duration query - } - EndTracing(); - - std::unique_ptr<TraceAnalyzer> analyzer( - TraceAnalyzer::Create(output_.json_output)); - ASSERT_TRUE(analyzer.get()); - analyzer->AssociateBeginEndEvents(); - - TraceEventVector found; - analyzer->FindEvents( - Query::MatchBeginWithEnd() && - Query::EventDuration() > - Query::Int(static_cast<int>(duration_cutoff_us)) && - (Query::EventCategory() == Query::String("cat1") || - Query::EventCategory() == Query::String("cat2") || - Query::EventCategory() == Query::String("cat3")), - &found); - ASSERT_EQ(2u, found.size()); - EXPECT_STREQ("name1", found[0]->name.c_str()); - EXPECT_STREQ("name3", found[1]->name.c_str()); -} - -// Test that duration queries work. -TEST_F(TraceEventAnalyzerTest, CompleteDuration) { - ManualSetUp(); - - const base::TimeDelta kSleepTime = base::TimeDelta::FromMilliseconds(200); - // We will search for events that have a duration of greater than 90% of the - // sleep time, so that there is no flakiness. - int64_t duration_cutoff_us = (kSleepTime.InMicroseconds() * 9) / 10; - - BeginTracing(); - { - TRACE_EVENT0("cat1", "name1"); // found by duration query - TRACE_EVENT0("noise", "name2"); // not searched for, just noise - { - TRACE_EVENT0("cat2", "name3"); // found by duration query - // next event not searched for, just noise - TRACE_EVENT_INSTANT0("noise", "name4", TRACE_EVENT_SCOPE_THREAD); - base::PlatformThread::Sleep(kSleepTime); - TRACE_EVENT0("cat2", "name5"); // not found (duration too short) - } - } - EndTracing(); - - std::unique_ptr<TraceAnalyzer> analyzer( - TraceAnalyzer::Create(output_.json_output)); - ASSERT_TRUE(analyzer.get()); - analyzer->AssociateBeginEndEvents(); - - TraceEventVector found; - analyzer->FindEvents( - Query::EventCompleteDuration() > - Query::Int(static_cast<int>(duration_cutoff_us)) && - (Query::EventCategory() == Query::String("cat1") || - Query::EventCategory() == Query::String("cat2") || - Query::EventCategory() == Query::String("cat3")), - &found); - ASSERT_EQ(2u, found.size()); - EXPECT_STREQ("name1", found[0]->name.c_str()); - EXPECT_STREQ("name3", found[1]->name.c_str()); -} - -// Test AssociateBeginEndEvents -TEST_F(TraceEventAnalyzerTest, BeginEndAssocations) { - ManualSetUp(); - - BeginTracing(); - { - TRACE_EVENT_END0("cat1", "name1"); // does not match out of order begin - TRACE_EVENT_BEGIN0("cat1", "name2"); - TRACE_EVENT_INSTANT0("cat1", "name3", TRACE_EVENT_SCOPE_THREAD); - TRACE_EVENT_BEGIN0("cat1", "name1"); - TRACE_EVENT_END0("cat1", "name2"); - } - EndTracing(); - - std::unique_ptr<TraceAnalyzer> analyzer( - TraceAnalyzer::Create(output_.json_output)); - ASSERT_TRUE(analyzer.get()); - analyzer->AssociateBeginEndEvents(); - - TraceEventVector found; - analyzer->FindEvents(Query::MatchBeginWithEnd(), &found); - ASSERT_EQ(1u, found.size()); - EXPECT_STREQ("name2", found[0]->name.c_str()); -} - -// Test MergeAssociatedEventArgs -TEST_F(TraceEventAnalyzerTest, MergeAssociatedEventArgs) { - ManualSetUp(); - - const char arg_string[] = "arg_string"; - BeginTracing(); - { - TRACE_EVENT_BEGIN0("cat1", "name1"); - TRACE_EVENT_END1("cat1", "name1", "arg", arg_string); - } - EndTracing(); - - std::unique_ptr<TraceAnalyzer> analyzer( - TraceAnalyzer::Create(output_.json_output)); - ASSERT_TRUE(analyzer.get()); - analyzer->AssociateBeginEndEvents(); - - TraceEventVector found; - analyzer->FindEvents(Query::MatchBeginName("name1"), &found); - ASSERT_EQ(1u, found.size()); - std::string arg_actual; - EXPECT_FALSE(found[0]->GetArgAsString("arg", &arg_actual)); - - analyzer->MergeAssociatedEventArgs(); - EXPECT_TRUE(found[0]->GetArgAsString("arg", &arg_actual)); - EXPECT_STREQ(arg_string, arg_actual.c_str()); -} - -// Test AssociateAsyncBeginEndEvents -TEST_F(TraceEventAnalyzerTest, AsyncBeginEndAssocations) { - ManualSetUp(); - - BeginTracing(); - { - TRACE_EVENT_ASYNC_END0("cat1", "name1", 0xA); // no match / out of order - TRACE_EVENT_ASYNC_BEGIN0("cat1", "name1", 0xB); - TRACE_EVENT_ASYNC_BEGIN0("cat1", "name1", 0xC); - TRACE_EVENT_INSTANT0("cat1", "name1", TRACE_EVENT_SCOPE_THREAD); // noise - TRACE_EVENT0("cat1", "name1"); // noise - TRACE_EVENT_ASYNC_END0("cat1", "name1", 0xB); - TRACE_EVENT_ASYNC_END0("cat1", "name1", 0xC); - TRACE_EVENT_ASYNC_BEGIN0("cat1", "name1", 0xA); // no match / out of order - } - EndTracing(); - - std::unique_ptr<TraceAnalyzer> analyzer( - TraceAnalyzer::Create(output_.json_output)); - ASSERT_TRUE(analyzer.get()); - analyzer->AssociateAsyncBeginEndEvents(); - - TraceEventVector found; - analyzer->FindEvents(Query::MatchAsyncBeginWithNext(), &found); - ASSERT_EQ(2u, found.size()); - EXPECT_STRCASEEQ("0xb", found[0]->id.c_str()); - EXPECT_STRCASEEQ("0xc", found[1]->id.c_str()); -} - -// Test AssociateAsyncBeginEndEvents -TEST_F(TraceEventAnalyzerTest, AsyncBeginEndAssocationsWithSteps) { - ManualSetUp(); - - BeginTracing(); - { - TRACE_EVENT_ASYNC_STEP_INTO0("c", "n", 0xA, "s1"); - TRACE_EVENT_ASYNC_END0("c", "n", 0xA); - TRACE_EVENT_ASYNC_BEGIN0("c", "n", 0xB); - TRACE_EVENT_ASYNC_BEGIN0("c", "n", 0xC); - TRACE_EVENT_ASYNC_STEP_PAST0("c", "n", 0xB, "s1"); - TRACE_EVENT_ASYNC_STEP_INTO0("c", "n", 0xC, "s1"); - TRACE_EVENT_ASYNC_STEP_INTO1("c", "n", 0xC, "s2", "a", 1); - TRACE_EVENT_ASYNC_END0("c", "n", 0xB); - TRACE_EVENT_ASYNC_END0("c", "n", 0xC); - TRACE_EVENT_ASYNC_BEGIN0("c", "n", 0xA); - TRACE_EVENT_ASYNC_STEP_INTO0("c", "n", 0xA, "s2"); - } - EndTracing(); - - std::unique_ptr<TraceAnalyzer> analyzer( - TraceAnalyzer::Create(output_.json_output)); - ASSERT_TRUE(analyzer.get()); - analyzer->AssociateAsyncBeginEndEvents(); - - TraceEventVector found; - analyzer->FindEvents(Query::MatchAsyncBeginWithNext(), &found); - ASSERT_EQ(3u, found.size()); - - EXPECT_STRCASEEQ("0xb", found[0]->id.c_str()); - EXPECT_EQ(TRACE_EVENT_PHASE_ASYNC_STEP_PAST, found[0]->other_event->phase); - EXPECT_EQ(found[0], found[0]->other_event->prev_event); - EXPECT_TRUE(found[0]->other_event->other_event); - EXPECT_EQ(TRACE_EVENT_PHASE_ASYNC_END, - found[0]->other_event->other_event->phase); - EXPECT_EQ(found[0]->other_event, - found[0]->other_event->other_event->prev_event); - - EXPECT_STRCASEEQ("0xc", found[1]->id.c_str()); - EXPECT_EQ(TRACE_EVENT_PHASE_ASYNC_STEP_INTO, found[1]->other_event->phase); - EXPECT_EQ(found[1], found[1]->other_event->prev_event); - EXPECT_TRUE(found[1]->other_event->other_event); - EXPECT_EQ(TRACE_EVENT_PHASE_ASYNC_STEP_INTO, - found[1]->other_event->other_event->phase); - EXPECT_EQ(found[1]->other_event, - found[1]->other_event->other_event->prev_event); - double arg_actual = 0; - EXPECT_TRUE(found[1]->other_event->other_event->GetArgAsNumber( - "a", &arg_actual)); - EXPECT_EQ(1.0, arg_actual); - EXPECT_TRUE(found[1]->other_event->other_event->other_event); - EXPECT_EQ(TRACE_EVENT_PHASE_ASYNC_END, - found[1]->other_event->other_event->other_event->phase); - - EXPECT_STRCASEEQ("0xa", found[2]->id.c_str()); - EXPECT_EQ(TRACE_EVENT_PHASE_ASYNC_STEP_INTO, found[2]->other_event->phase); -} - -// Test that the TraceAnalyzer custom associations work. -TEST_F(TraceEventAnalyzerTest, CustomAssociations) { - ManualSetUp(); - - // Add events that begin/end in pipelined ordering with unique ID parameter - // to match up the begin/end pairs. - BeginTracing(); - { - // no begin match - TRACE_EVENT_INSTANT1("cat1", "end", TRACE_EVENT_SCOPE_THREAD, "id", 1); - // end is cat4 - TRACE_EVENT_INSTANT1("cat2", "begin", TRACE_EVENT_SCOPE_THREAD, "id", 2); - // end is cat5 - TRACE_EVENT_INSTANT1("cat3", "begin", TRACE_EVENT_SCOPE_THREAD, "id", 3); - TRACE_EVENT_INSTANT1("cat4", "end", TRACE_EVENT_SCOPE_THREAD, "id", 2); - TRACE_EVENT_INSTANT1("cat5", "end", TRACE_EVENT_SCOPE_THREAD, "id", 3); - // no end match - TRACE_EVENT_INSTANT1("cat6", "begin", TRACE_EVENT_SCOPE_THREAD, "id", 1); - } - EndTracing(); - - std::unique_ptr<TraceAnalyzer> analyzer( - TraceAnalyzer::Create(output_.json_output)); - ASSERT_TRUE(analyzer.get()); - - // begin, end, and match queries to find proper begin/end pairs. - Query begin(Query::EventName() == Query::String("begin")); - Query end(Query::EventName() == Query::String("end")); - Query match(Query::EventArg("id") == Query::OtherArg("id")); - analyzer->AssociateEvents(begin, end, match); - - TraceEventVector found; - - // cat1 has no other_event. - analyzer->FindEvents(Query::EventCategory() == Query::String("cat1") && - Query::EventHasOther(), &found); - EXPECT_EQ(0u, found.size()); - - // cat1 has no other_event. - analyzer->FindEvents(Query::EventCategory() == Query::String("cat1") && - !Query::EventHasOther(), &found); - EXPECT_EQ(1u, found.size()); - - // cat6 has no other_event. - analyzer->FindEvents(Query::EventCategory() == Query::String("cat6") && - !Query::EventHasOther(), &found); - EXPECT_EQ(1u, found.size()); - - // cat2 and cat4 are associated. - analyzer->FindEvents(Query::EventCategory() == Query::String("cat2") && - Query::OtherCategory() == Query::String("cat4"), &found); - EXPECT_EQ(1u, found.size()); - - // cat4 and cat2 are not associated. - analyzer->FindEvents(Query::EventCategory() == Query::String("cat4") && - Query::OtherCategory() == Query::String("cat2"), &found); - EXPECT_EQ(0u, found.size()); - - // cat3 and cat5 are associated. - analyzer->FindEvents(Query::EventCategory() == Query::String("cat3") && - Query::OtherCategory() == Query::String("cat5"), &found); - EXPECT_EQ(1u, found.size()); - - // cat5 and cat3 are not associated. - analyzer->FindEvents(Query::EventCategory() == Query::String("cat5") && - Query::OtherCategory() == Query::String("cat3"), &found); - EXPECT_EQ(0u, found.size()); -} - -// Verify that Query literals and types are properly casted. -TEST_F(TraceEventAnalyzerTest, Literals) { - ManualSetUp(); - - // Since these queries don't refer to the event data, the dummy event below - // will never be accessed. - TraceEvent dummy; - char char_num = 5; - short short_num = -5; - EXPECT_TRUE((Query::Double(5.0) == Query::Int(char_num)).Evaluate(dummy)); - EXPECT_TRUE((Query::Double(-5.0) == Query::Int(short_num)).Evaluate(dummy)); - EXPECT_TRUE((Query::Double(1.0) == Query::Uint(1u)).Evaluate(dummy)); - EXPECT_TRUE((Query::Double(1.0) == Query::Int(1)).Evaluate(dummy)); - EXPECT_TRUE((Query::Double(-1.0) == Query::Int(-1)).Evaluate(dummy)); - EXPECT_TRUE((Query::Double(1.0) == Query::Double(1.0f)).Evaluate(dummy)); - EXPECT_TRUE((Query::Bool(true) == Query::Int(1)).Evaluate(dummy)); - EXPECT_TRUE((Query::Bool(false) == Query::Int(0)).Evaluate(dummy)); - EXPECT_TRUE((Query::Bool(true) == Query::Double(1.0f)).Evaluate(dummy)); - EXPECT_TRUE((Query::Bool(false) == Query::Double(0.0f)).Evaluate(dummy)); -} - -// Test GetRateStats. -TEST_F(TraceEventAnalyzerTest, RateStats) { - std::vector<TraceEvent> events; - events.reserve(100); - TraceEventVector event_ptrs; - double timestamp = 0.0; - double little_delta = 1.0; - double big_delta = 10.0; - double tiny_delta = 0.1; - RateStats stats; - RateStatsOptions options; - - // Insert 10 events, each apart by little_delta. - for (int i = 0; i < 10; ++i) { - timestamp += little_delta; - TraceEvent event; - event.timestamp = timestamp; - events.push_back(std::move(event)); - event_ptrs.push_back(&events.back()); - } - - ASSERT_TRUE(GetRateStats(event_ptrs, &stats, NULL)); - EXPECT_EQ(little_delta, stats.mean_us); - EXPECT_EQ(little_delta, stats.min_us); - EXPECT_EQ(little_delta, stats.max_us); - EXPECT_EQ(0.0, stats.standard_deviation_us); - - // Add an event apart by big_delta. - { - timestamp += big_delta; - TraceEvent event; - event.timestamp = timestamp; - events.push_back(std::move(event)); - event_ptrs.push_back(&events.back()); - } - - ASSERT_TRUE(GetRateStats(event_ptrs, &stats, NULL)); - EXPECT_LT(little_delta, stats.mean_us); - EXPECT_EQ(little_delta, stats.min_us); - EXPECT_EQ(big_delta, stats.max_us); - EXPECT_LT(0.0, stats.standard_deviation_us); - - // Trim off the biggest delta and verify stats. - options.trim_min = 0; - options.trim_max = 1; - ASSERT_TRUE(GetRateStats(event_ptrs, &stats, &options)); - EXPECT_EQ(little_delta, stats.mean_us); - EXPECT_EQ(little_delta, stats.min_us); - EXPECT_EQ(little_delta, stats.max_us); - EXPECT_EQ(0.0, stats.standard_deviation_us); - - // Add an event apart by tiny_delta. - { - timestamp += tiny_delta; - TraceEvent event; - event.timestamp = timestamp; - events.push_back(std::move(event)); - event_ptrs.push_back(&events.back()); - } - - // Trim off both the biggest and tiniest delta and verify stats. - options.trim_min = 1; - options.trim_max = 1; - ASSERT_TRUE(GetRateStats(event_ptrs, &stats, &options)); - EXPECT_EQ(little_delta, stats.mean_us); - EXPECT_EQ(little_delta, stats.min_us); - EXPECT_EQ(little_delta, stats.max_us); - EXPECT_EQ(0.0, stats.standard_deviation_us); - - // Verify smallest allowed number of events. - { - TraceEvent event; - TraceEventVector few_event_ptrs; - few_event_ptrs.push_back(&event); - few_event_ptrs.push_back(&event); - ASSERT_FALSE(GetRateStats(few_event_ptrs, &stats, NULL)); - few_event_ptrs.push_back(&event); - ASSERT_TRUE(GetRateStats(few_event_ptrs, &stats, NULL)); - - // Trim off more than allowed and verify failure. - options.trim_min = 0; - options.trim_max = 1; - ASSERT_FALSE(GetRateStats(few_event_ptrs, &stats, &options)); - } -} - -// Test FindFirstOf and FindLastOf. -TEST_F(TraceEventAnalyzerTest, FindOf) { - size_t num_events = 100; - size_t index = 0; - TraceEventVector event_ptrs; - EXPECT_FALSE(FindFirstOf(event_ptrs, Query::Bool(true), 0, &index)); - EXPECT_FALSE(FindFirstOf(event_ptrs, Query::Bool(true), 10, &index)); - EXPECT_FALSE(FindLastOf(event_ptrs, Query::Bool(true), 0, &index)); - EXPECT_FALSE(FindLastOf(event_ptrs, Query::Bool(true), 10, &index)); - - std::vector<TraceEvent> events; - events.resize(num_events); - for (size_t i = 0; i < events.size(); ++i) - event_ptrs.push_back(&events[i]); - size_t bam_index = num_events/2; - events[bam_index].name = "bam"; - Query query_bam = Query::EventName() == Query::String(events[bam_index].name); - - // FindFirstOf - EXPECT_FALSE(FindFirstOf(event_ptrs, Query::Bool(false), 0, &index)); - EXPECT_TRUE(FindFirstOf(event_ptrs, Query::Bool(true), 0, &index)); - EXPECT_EQ(0u, index); - EXPECT_TRUE(FindFirstOf(event_ptrs, Query::Bool(true), 5, &index)); - EXPECT_EQ(5u, index); - - EXPECT_FALSE(FindFirstOf(event_ptrs, query_bam, bam_index + 1, &index)); - EXPECT_TRUE(FindFirstOf(event_ptrs, query_bam, 0, &index)); - EXPECT_EQ(bam_index, index); - EXPECT_TRUE(FindFirstOf(event_ptrs, query_bam, bam_index, &index)); - EXPECT_EQ(bam_index, index); - - // FindLastOf - EXPECT_FALSE(FindLastOf(event_ptrs, Query::Bool(false), 1000, &index)); - EXPECT_TRUE(FindLastOf(event_ptrs, Query::Bool(true), 1000, &index)); - EXPECT_EQ(num_events - 1, index); - EXPECT_TRUE(FindLastOf(event_ptrs, Query::Bool(true), num_events - 5, - &index)); - EXPECT_EQ(num_events - 5, index); - - EXPECT_FALSE(FindLastOf(event_ptrs, query_bam, bam_index - 1, &index)); - EXPECT_TRUE(FindLastOf(event_ptrs, query_bam, num_events, &index)); - EXPECT_EQ(bam_index, index); - EXPECT_TRUE(FindLastOf(event_ptrs, query_bam, bam_index, &index)); - EXPECT_EQ(bam_index, index); -} - -// Test FindClosest. -TEST_F(TraceEventAnalyzerTest, FindClosest) { - size_t index_1 = 0; - size_t index_2 = 0; - TraceEventVector event_ptrs; - EXPECT_FALSE(FindClosest(event_ptrs, Query::Bool(true), 0, - &index_1, &index_2)); - - size_t num_events = 5; - std::vector<TraceEvent> events; - events.resize(num_events); - for (size_t i = 0; i < events.size(); ++i) { - // timestamps go up exponentially so the lower index is always closer in - // time than the higher index. - events[i].timestamp = static_cast<double>(i) * static_cast<double>(i); - event_ptrs.push_back(&events[i]); - } - events[0].name = "one"; - events[2].name = "two"; - events[4].name = "three"; - Query query_named = Query::EventName() != Query::String(std::string()); - Query query_one = Query::EventName() == Query::String("one"); - - // Only one event matches query_one, so two closest can't be found. - EXPECT_FALSE(FindClosest(event_ptrs, query_one, 0, &index_1, &index_2)); - - EXPECT_TRUE(FindClosest(event_ptrs, query_one, 3, &index_1, NULL)); - EXPECT_EQ(0u, index_1); - - EXPECT_TRUE(FindClosest(event_ptrs, query_named, 1, &index_1, &index_2)); - EXPECT_EQ(0u, index_1); - EXPECT_EQ(2u, index_2); - - EXPECT_TRUE(FindClosest(event_ptrs, query_named, 4, &index_1, &index_2)); - EXPECT_EQ(4u, index_1); - EXPECT_EQ(2u, index_2); - - EXPECT_TRUE(FindClosest(event_ptrs, query_named, 3, &index_1, &index_2)); - EXPECT_EQ(2u, index_1); - EXPECT_EQ(0u, index_2); -} - -// Test CountMatches. -TEST_F(TraceEventAnalyzerTest, CountMatches) { - TraceEventVector event_ptrs; - EXPECT_EQ(0u, CountMatches(event_ptrs, Query::Bool(true), 0, 10)); - - size_t num_events = 5; - size_t num_named = 3; - std::vector<TraceEvent> events; - events.resize(num_events); - for (size_t i = 0; i < events.size(); ++i) - event_ptrs.push_back(&events[i]); - events[0].name = "one"; - events[2].name = "two"; - events[4].name = "three"; - Query query_named = Query::EventName() != Query::String(std::string()); - Query query_one = Query::EventName() == Query::String("one"); - - EXPECT_EQ(0u, CountMatches(event_ptrs, Query::Bool(false))); - EXPECT_EQ(num_events, CountMatches(event_ptrs, Query::Bool(true))); - EXPECT_EQ(num_events - 1, CountMatches(event_ptrs, Query::Bool(true), - 1, num_events)); - EXPECT_EQ(1u, CountMatches(event_ptrs, query_one)); - EXPECT_EQ(num_events - 1, CountMatches(event_ptrs, !query_one)); - EXPECT_EQ(num_named, CountMatches(event_ptrs, query_named)); -} - -TEST_F(TraceEventAnalyzerTest, ComplexArgument) { - ManualSetUp(); - - BeginTracing(); - { - std::unique_ptr<base::trace_event::TracedValue> value( - new base::trace_event::TracedValue); - value->SetString("property", "value"); - TRACE_EVENT1("cat", "name", "arg", std::move(value)); - } - EndTracing(); - - std::unique_ptr<TraceAnalyzer> analyzer( - TraceAnalyzer::Create(output_.json_output)); - ASSERT_TRUE(analyzer.get()); - - TraceEventVector events; - analyzer->FindEvents(Query::EventName() == Query::String("name"), &events); - - EXPECT_EQ(1u, events.size()); - EXPECT_EQ("cat", events[0]->category); - EXPECT_EQ("name", events[0]->name); - EXPECT_TRUE(events[0]->HasArg("arg")); - - std::unique_ptr<base::Value> arg; - events[0]->GetArgAsValue("arg", &arg); - base::DictionaryValue* arg_dict; - EXPECT_TRUE(arg->GetAsDictionary(&arg_dict)); - std::string property; - EXPECT_TRUE(arg_dict->GetString("property", &property)); - EXPECT_EQ("value", property); -} - -} // namespace trace_analyzer diff --git a/base/threading/thread_id_name_manager.cc b/base/threading/thread_id_name_manager.cc index 107e0dc498..c0ca42da30 100644 --- a/base/threading/thread_id_name_manager.cc +++ b/base/threading/thread_id_name_manager.cc @@ -10,7 +10,8 @@ #include "base/logging.h" #include "base/memory/singleton.h" #include "base/strings/string_util.h" -#include "base/trace_event/heap_profiler_allocation_context_tracker.h" +// Unsupported in libchrome. +// #include "base/trace_event/heap_profiler_allocation_context_tracker.h" namespace base { namespace { @@ -80,8 +81,9 @@ void ThreadIdNameManager::SetName(PlatformThreadId id, // call GetName(which holds a lock) during the first allocation because it can // cause a deadlock when the first allocation happens in the // ThreadIdNameManager itself when holding the lock. - trace_event::AllocationContextTracker::SetCurrentThreadName( - leaked_str->c_str()); + // Unsupported in libchrome. + // trace_event::AllocationContextTracker::SetCurrentThreadName( + // leaked_str->c_str()); } const char* ThreadIdNameManager::GetName(PlatformThreadId id) { diff --git a/base/trace_event/category_registry.cc b/base/trace_event/category_registry.cc deleted file mode 100644 index e7c14606d6..0000000000 --- a/base/trace_event/category_registry.cc +++ /dev/null @@ -1,156 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/trace_event/category_registry.h" - -#include <string.h> - -#include <type_traits> - -#include "base/atomicops.h" -#include "base/debug/leak_annotations.h" -#include "base/logging.h" -#include "base/third_party/dynamic_annotations/dynamic_annotations.h" -#include "base/trace_event/trace_category.h" - -namespace base { -namespace trace_event { - -namespace { - -constexpr size_t kMaxCategories = 200; -const int kNumBuiltinCategories = 4; - -// |g_categories| might end up causing creating dynamic initializers if not POD. -static_assert(std::is_pod<TraceCategory>::value, "TraceCategory must be POD"); - -// These entries must be kept consistent with the kCategory* consts below. -TraceCategory g_categories[kMaxCategories] = { - {0, 0, "tracing categories exhausted; must increase kMaxCategories"}, - {0, 0, "tracing already shutdown"}, // See kCategoryAlreadyShutdown below. - {0, 0, "__metadata"}, // See kCategoryMetadata below. - {0, 0, "toplevel"}, // Warmup the toplevel category. -}; - -base::subtle::AtomicWord g_category_index = kNumBuiltinCategories; - -bool IsValidCategoryPtr(const TraceCategory* category) { - // If any of these are hit, something has cached a corrupt category pointer. - uintptr_t ptr = reinterpret_cast<uintptr_t>(category); - return ptr % sizeof(void*) == 0 && - ptr >= reinterpret_cast<uintptr_t>(&g_categories[0]) && - ptr <= reinterpret_cast<uintptr_t>(&g_categories[kMaxCategories - 1]); -} - -} // namespace - -// static -TraceCategory* const CategoryRegistry::kCategoryExhausted = &g_categories[0]; -TraceCategory* const CategoryRegistry::kCategoryAlreadyShutdown = - &g_categories[1]; -TraceCategory* const CategoryRegistry::kCategoryMetadata = &g_categories[2]; - -// static -void CategoryRegistry::Initialize() { - // Trace is enabled or disabled on one thread while other threads are - // accessing the enabled flag. We don't care whether edge-case events are - // traced or not, so we allow races on the enabled flag to keep the trace - // macros fast. - for (size_t i = 0; i < kMaxCategories; ++i) { - ANNOTATE_BENIGN_RACE(g_categories[i].state_ptr(), - "trace_event category enabled"); - // If this DCHECK is hit in a test it means that ResetForTesting() is not - // called and the categories state leaks between test fixtures. - DCHECK(!g_categories[i].is_enabled()); - } -} - -// static -void CategoryRegistry::ResetForTesting() { - // reset_for_testing clears up only the enabled state and filters. The - // categories themselves cannot be cleared up because the static pointers - // injected by the macros still point to them and cannot be reset. - for (size_t i = 0; i < kMaxCategories; ++i) - g_categories[i].reset_for_testing(); -} - -// static -TraceCategory* CategoryRegistry::GetCategoryByName(const char* category_name) { - DCHECK(!strchr(category_name, '"')) - << "Category names may not contain double quote"; - - // The g_categories is append only, avoid using a lock for the fast path. - size_t category_index = base::subtle::Acquire_Load(&g_category_index); - - // Search for pre-existing category group. - for (size_t i = 0; i < category_index; ++i) { - if (strcmp(g_categories[i].name(), category_name) == 0) { - return &g_categories[i]; - } - } - return nullptr; -} - -bool CategoryRegistry::GetOrCreateCategoryLocked( - const char* category_name, - CategoryInitializerFn category_initializer_fn, - TraceCategory** category) { - // This is the slow path: the lock is not held in the fastpath - // (GetCategoryByName), so more than one thread could have reached here trying - // to add the same category. - *category = GetCategoryByName(category_name); - if (*category) - return false; - - // Create a new category. - size_t category_index = base::subtle::Acquire_Load(&g_category_index); - if (category_index >= kMaxCategories) { - NOTREACHED() << "must increase kMaxCategories"; - *category = kCategoryExhausted; - return false; - } - - // TODO(primiano): this strdup should be removed. The only documented reason - // for it was TraceWatchEvent, which is gone. However, something might have - // ended up relying on this. Needs some auditing before removal. - const char* category_name_copy = strdup(category_name); - ANNOTATE_LEAKING_OBJECT_PTR(category_name_copy); - - *category = &g_categories[category_index]; - DCHECK(!(*category)->is_valid()); - DCHECK(!(*category)->is_enabled()); - (*category)->set_name(category_name_copy); - category_initializer_fn(*category); - - // Update the max index now. - base::subtle::Release_Store(&g_category_index, category_index + 1); - return true; -} - -// static -const TraceCategory* CategoryRegistry::GetCategoryByStatePtr( - const uint8_t* category_state) { - const TraceCategory* category = TraceCategory::FromStatePtr(category_state); - DCHECK(IsValidCategoryPtr(category)); - return category; -} - -// static -bool CategoryRegistry::IsBuiltinCategory(const TraceCategory* category) { - DCHECK(IsValidCategoryPtr(category)); - return category < &g_categories[kNumBuiltinCategories]; -} - -// static -CategoryRegistry::Range CategoryRegistry::GetAllCategories() { - // The |g_categories| array is append only. We have to only guarantee to - // not return an index to a category which is being initialized by - // GetOrCreateCategoryByName(). - size_t category_index = base::subtle::Acquire_Load(&g_category_index); - return CategoryRegistry::Range(&g_categories[0], - &g_categories[category_index]); -} - -} // namespace trace_event -} // namespace base diff --git a/base/trace_event/category_registry.h b/base/trace_event/category_registry.h deleted file mode 100644 index 9c08efa3e1..0000000000 --- a/base/trace_event/category_registry.h +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_TRACE_EVENT_CATEGORY_REGISTRY_H_ -#define BASE_TRACE_EVENT_CATEGORY_REGISTRY_H_ - -#include <stddef.h> -#include <stdint.h> - -#include "base/base_export.h" -#include "base/logging.h" - -namespace base { -namespace trace_event { - -struct TraceCategory; -class TraceCategoryTest; -class TraceLog; - -// Allows fast and thread-safe acces to the state of all tracing categories. -// All the methods in this class can be concurrently called on multiple threads, -// unless otherwise noted (e.g., GetOrCreateCategoryLocked). -// The reason why this is a fully static class with global state is to allow to -// statically define known categories as global linker-initialized structs, -// without requiring static initializers. -class BASE_EXPORT CategoryRegistry { - public: - // Allows for-each iterations over a slice of the categories array. - class Range { - public: - Range(TraceCategory* begin, TraceCategory* end) : begin_(begin), end_(end) { - DCHECK_LE(begin, end); - } - TraceCategory* begin() const { return begin_; } - TraceCategory* end() const { return end_; } - - private: - TraceCategory* const begin_; - TraceCategory* const end_; - }; - - // Known categories. - static TraceCategory* const kCategoryExhausted; - static TraceCategory* const kCategoryMetadata; - static TraceCategory* const kCategoryAlreadyShutdown; - - // Returns a category entry from the Category.state_ptr() pointer. - // TODO(primiano): trace macros should just keep a pointer to the entire - // TraceCategory, not just the enabled state pointer. That would remove the - // need for this function and make everything cleaner at no extra cost (as - // long as the |state_| is the first field of the struct, which can be - // guaranteed via static_assert, see TraceCategory ctor). - static const TraceCategory* GetCategoryByStatePtr( - const uint8_t* category_state); - - // Returns a category from its name or nullptr if not found. - // The output |category| argument is an undefinitely lived pointer to the - // TraceCategory owned by the registry. TRACE_EVENTx macros will cache this - // pointer and use it for checks in their fast-paths. - static TraceCategory* GetCategoryByName(const char* category_name); - - static bool IsBuiltinCategory(const TraceCategory*); - - private: - friend class TraceCategoryTest; - friend class TraceLog; - using CategoryInitializerFn = void (*)(TraceCategory*); - - // Only for debugging/testing purposes, is a no-op on release builds. - static void Initialize(); - - // Resets the state of all categories, to clear up the state between tests. - static void ResetForTesting(); - - // Used to get/create a category in the slow-path. If the category exists - // already, this has the same effect of GetCategoryByName and returns false. - // If not, a new category is created and the CategoryInitializerFn is invoked - // before retuning true. The caller must guarantee serialization: either call - // this method from a single thread or hold a lock when calling this. - static bool GetOrCreateCategoryLocked(const char* category_name, - CategoryInitializerFn, - TraceCategory**); - - // Allows to iterate over the valid categories in a for-each loop. - // This includes builtin categories such as __metadata. - static Range GetAllCategories(); -}; - -} // namespace trace_event -} // namespace base - -#endif // BASE_TRACE_EVENT_CATEGORY_REGISTRY_H_ diff --git a/base/trace_event/event_name_filter.cc b/base/trace_event/event_name_filter.cc deleted file mode 100644 index 8d0058c147..0000000000 --- a/base/trace_event/event_name_filter.cc +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/trace_event/event_name_filter.h" - -#include "base/trace_event/trace_event_impl.h" - -namespace base { -namespace trace_event { - -// static -const char EventNameFilter::kName[] = "event_whitelist_predicate"; - -EventNameFilter::EventNameFilter( - std::unique_ptr<EventNamesWhitelist> event_names_whitelist) - : event_names_whitelist_(std::move(event_names_whitelist)) {} - -EventNameFilter::~EventNameFilter() {} - -bool EventNameFilter::FilterTraceEvent(const TraceEvent& trace_event) const { - return event_names_whitelist_->count(trace_event.name()) != 0; -} - -} // namespace trace_event -} // namespace base diff --git a/base/trace_event/event_name_filter.h b/base/trace_event/event_name_filter.h deleted file mode 100644 index 19333b3e03..0000000000 --- a/base/trace_event/event_name_filter.h +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_TRACE_EVENT_EVENT_NAME_FILTER_H_ -#define BASE_TRACE_EVENT_EVENT_NAME_FILTER_H_ - -#include <memory> -#include <string> -#include <unordered_set> - -#include "base/base_export.h" -#include "base/macros.h" -#include "base/trace_event/trace_event_filter.h" - -namespace base { -namespace trace_event { - -class TraceEvent; - -// Filters trace events by checking the full name against a whitelist. -// The current implementation is quite simple and dumb and just uses a -// hashtable which requires char* to std::string conversion. It could be smarter -// and use a bloom filter trie. However, today this is used too rarely to -// justify that cost. -class BASE_EXPORT EventNameFilter : public TraceEventFilter { - public: - using EventNamesWhitelist = std::unordered_set<std::string>; - static const char kName[]; - - EventNameFilter(std::unique_ptr<EventNamesWhitelist>); - ~EventNameFilter() override; - - // TraceEventFilter implementation. - bool FilterTraceEvent(const TraceEvent&) const override; - - private: - std::unique_ptr<const EventNamesWhitelist> event_names_whitelist_; - - DISALLOW_COPY_AND_ASSIGN(EventNameFilter); -}; - -} // namespace trace_event -} // namespace base - -#endif // BASE_TRACE_EVENT_EVENT_NAME_FILTER_H_ diff --git a/base/trace_event/event_name_filter_unittest.cc b/base/trace_event/event_name_filter_unittest.cc deleted file mode 100644 index 0bc2a4dafc..0000000000 --- a/base/trace_event/event_name_filter_unittest.cc +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/trace_event/event_name_filter.h" - -#include "base/memory/ptr_util.h" -#include "base/trace_event/trace_event_impl.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { -namespace trace_event { - -const TraceEvent& MakeTraceEvent(const char* name) { - static TraceEvent event; - event.Reset(); - event.Initialize(0, TimeTicks(), ThreadTicks(), 'b', nullptr, name, "", 0, 0, - 0, nullptr, nullptr, nullptr, nullptr, 0); - return event; -} - -TEST(TraceEventNameFilterTest, Whitelist) { - auto empty_whitelist = MakeUnique<EventNameFilter::EventNamesWhitelist>(); - auto filter = MakeUnique<EventNameFilter>(std::move(empty_whitelist)); - - // No events should be filtered if the whitelist is empty. - EXPECT_FALSE(filter->FilterTraceEvent(MakeTraceEvent("foo"))); - - auto whitelist = MakeUnique<EventNameFilter::EventNamesWhitelist>(); - whitelist->insert("foo"); - whitelist->insert("bar"); - filter = MakeUnique<EventNameFilter>(std::move(whitelist)); - EXPECT_TRUE(filter->FilterTraceEvent(MakeTraceEvent("foo"))); - EXPECT_FALSE(filter->FilterTraceEvent(MakeTraceEvent("fooz"))); - EXPECT_FALSE(filter->FilterTraceEvent(MakeTraceEvent("afoo"))); - EXPECT_TRUE(filter->FilterTraceEvent(MakeTraceEvent("bar"))); - EXPECT_FALSE(filter->FilterTraceEvent(MakeTraceEvent("foobar"))); -} - -} // namespace trace_event -} // namespace base diff --git a/base/trace_event/heap_profiler.h b/base/trace_event/heap_profiler.h index cf57524627..a9cfcfde05 100644 --- a/base/trace_event/heap_profiler.h +++ b/base/trace_event/heap_profiler.h @@ -5,6 +5,22 @@ #ifndef BASE_TRACE_EVENT_HEAP_PROFILER_H #define BASE_TRACE_EVENT_HEAP_PROFILER_H +// Replace with stub implementation. +#if 1 +#define TRACE_HEAP_PROFILER_API_SCOPED_TASK_EXECUTION \ + trace_event_internal::HeapProfilerScopedTaskExecutionTracker + +namespace trace_event_internal { + +class HeapProfilerScopedTaskExecutionTracker { + public: + explicit HeapProfilerScopedTaskExecutionTracker(const char*) {} +}; + +} // namespace trace_event_internal + +#else + #include "base/compiler_specific.h" #include "base/trace_event/heap_profiler_allocation_context_tracker.h" @@ -86,4 +102,5 @@ class BASE_EXPORT HeapProfilerScopedIgnore { } // namespace trace_event_internal +#endif #endif // BASE_TRACE_EVENT_HEAP_PROFILER_H diff --git a/base/trace_event/heap_profiler_allocation_context.cc b/base/trace_event/heap_profiler_allocation_context.cc deleted file mode 100644 index 0f330a817e..0000000000 --- a/base/trace_event/heap_profiler_allocation_context.cc +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/trace_event/heap_profiler_allocation_context.h" - -#include <cstring> - -#include "base/hash.h" -#include "base/macros.h" - -namespace base { -namespace trace_event { - -bool operator < (const StackFrame& lhs, const StackFrame& rhs) { - return lhs.value < rhs.value; -} - -bool operator == (const StackFrame& lhs, const StackFrame& rhs) { - return lhs.value == rhs.value; -} - -bool operator != (const StackFrame& lhs, const StackFrame& rhs) { - return !(lhs.value == rhs.value); -} - -Backtrace::Backtrace(): frame_count(0) {} - -bool operator==(const Backtrace& lhs, const Backtrace& rhs) { - if (lhs.frame_count != rhs.frame_count) return false; - return std::equal(lhs.frames, lhs.frames + lhs.frame_count, rhs.frames); -} - -bool operator!=(const Backtrace& lhs, const Backtrace& rhs) { - return !(lhs == rhs); -} - -AllocationContext::AllocationContext(): type_name(nullptr) {} - -AllocationContext::AllocationContext(const Backtrace& backtrace, - const char* type_name) - : backtrace(backtrace), type_name(type_name) {} - -bool operator==(const AllocationContext& lhs, const AllocationContext& rhs) { - return (lhs.backtrace == rhs.backtrace) && (lhs.type_name == rhs.type_name); -} - -bool operator!=(const AllocationContext& lhs, const AllocationContext& rhs) { - return !(lhs == rhs); -} -} // namespace trace_event -} // namespace base - -namespace BASE_HASH_NAMESPACE { -using base::trace_event::AllocationContext; -using base::trace_event::Backtrace; -using base::trace_event::StackFrame; - -size_t hash<StackFrame>::operator()(const StackFrame& frame) const { - return hash<const void*>()(frame.value); -} - -size_t hash<Backtrace>::operator()(const Backtrace& backtrace) const { - const void* values[Backtrace::kMaxFrameCount]; - for (size_t i = 0; i != backtrace.frame_count; ++i) { - values[i] = backtrace.frames[i].value; - } - return base::SuperFastHash( - reinterpret_cast<const char*>(values), - static_cast<int>(backtrace.frame_count * sizeof(*values))); -} - -size_t hash<AllocationContext>::operator()(const AllocationContext& ctx) const { - size_t backtrace_hash = hash<Backtrace>()(ctx.backtrace); - - // Multiplicative hash from [Knuth 1998]. Works best if |size_t| is 32 bits, - // because the magic number is a prime very close to 2^32 / golden ratio, but - // will still redistribute keys bijectively on 64-bit architectures because - // the magic number is coprime to 2^64. - size_t type_hash = reinterpret_cast<size_t>(ctx.type_name) * 2654435761; - - // Multiply one side to break the commutativity of +. Multiplication with a - // number coprime to |numeric_limits<size_t>::max() + 1| is bijective so - // randomness is preserved. - return (backtrace_hash * 3) + type_hash; -} - -} // BASE_HASH_NAMESPACE diff --git a/base/trace_event/heap_profiler_allocation_context.h b/base/trace_event/heap_profiler_allocation_context.h deleted file mode 100644 index 24e2dec73f..0000000000 --- a/base/trace_event/heap_profiler_allocation_context.h +++ /dev/null @@ -1,131 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_TRACE_EVENT_HEAP_PROFILER_ALLOCATION_CONTEXT_H_ -#define BASE_TRACE_EVENT_HEAP_PROFILER_ALLOCATION_CONTEXT_H_ - -#include <stddef.h> -#include <stdint.h> - -#include "base/base_export.h" -#include "base/containers/hash_tables.h" - -namespace base { -namespace trace_event { - -// When heap profiling is enabled, tracing keeps track of the allocation -// context for each allocation intercepted. It is generated by the -// |AllocationContextTracker| which keeps stacks of context in TLS. -// The tracker is initialized lazily. - -// The backtrace in the allocation context is a snapshot of the stack. For now, -// this is the pseudo stack where frames are created by trace event macros. In -// the future, we might add the option to use the native call stack. In that -// case, |Backtrace| and |AllocationContextTracker::GetContextSnapshot| might -// have different implementations that can be selected by a compile time flag. - -// The number of stack frames stored in the backtrace is a trade off between -// memory used for tracing and accuracy. Measurements done on a prototype -// revealed that: -// -// - In 60 percent of the cases, pseudo stack depth <= 7. -// - In 87 percent of the cases, pseudo stack depth <= 9. -// - In 95 percent of the cases, pseudo stack depth <= 11. -// -// See the design doc (https://goo.gl/4s7v7b) for more details. - -// Represents (pseudo) stack frame. Used in Backtrace class below. -// -// Conceptually stack frame is identified by its value, and type is used -// mostly to properly format the value. Value is expected to be a valid -// pointer from process' address space. -struct BASE_EXPORT StackFrame { - enum class Type { - TRACE_EVENT_NAME, // const char* string - THREAD_NAME, // const char* thread name - PROGRAM_COUNTER, // as returned by stack tracing (e.g. by StackTrace) - }; - - static StackFrame FromTraceEventName(const char* name) { - return {Type::TRACE_EVENT_NAME, name}; - } - static StackFrame FromThreadName(const char* name) { - return {Type::THREAD_NAME, name}; - } - static StackFrame FromProgramCounter(const void* pc) { - return {Type::PROGRAM_COUNTER, pc}; - } - - Type type; - const void* value; -}; - -bool BASE_EXPORT operator < (const StackFrame& lhs, const StackFrame& rhs); -bool BASE_EXPORT operator == (const StackFrame& lhs, const StackFrame& rhs); -bool BASE_EXPORT operator != (const StackFrame& lhs, const StackFrame& rhs); - -struct BASE_EXPORT Backtrace { - Backtrace(); - - // If the stack is higher than what can be stored here, the bottom frames - // (the ones closer to main()) are stored. Depth of 12 is enough for most - // pseudo traces (see above), but not for native traces, where we need more. - enum { kMaxFrameCount = 48 }; - StackFrame frames[kMaxFrameCount]; - size_t frame_count; -}; - -bool BASE_EXPORT operator==(const Backtrace& lhs, const Backtrace& rhs); -bool BASE_EXPORT operator!=(const Backtrace& lhs, const Backtrace& rhs); - -// The |AllocationContext| is context metadata that is kept for every allocation -// when heap profiling is enabled. To simplify memory management for book- -// keeping, this struct has a fixed size. -struct BASE_EXPORT AllocationContext { - AllocationContext(); - AllocationContext(const Backtrace& backtrace, const char* type_name); - - Backtrace backtrace; - - // Type name of the type stored in the allocated memory. A null pointer - // indicates "unknown type". Grouping is done by comparing pointers, not by - // deep string comparison. In a component build, where a type name can have a - // string literal in several dynamic libraries, this may distort grouping. - const char* type_name; -}; - -bool BASE_EXPORT operator==(const AllocationContext& lhs, - const AllocationContext& rhs); -bool BASE_EXPORT operator!=(const AllocationContext& lhs, - const AllocationContext& rhs); - -// Struct to store the size and count of the allocations. -struct AllocationMetrics { - size_t size; - size_t count; -}; - -} // namespace trace_event -} // namespace base - -namespace BASE_HASH_NAMESPACE { - -template <> -struct BASE_EXPORT hash<base::trace_event::StackFrame> { - size_t operator()(const base::trace_event::StackFrame& frame) const; -}; - -template <> -struct BASE_EXPORT hash<base::trace_event::Backtrace> { - size_t operator()(const base::trace_event::Backtrace& backtrace) const; -}; - -template <> -struct BASE_EXPORT hash<base::trace_event::AllocationContext> { - size_t operator()(const base::trace_event::AllocationContext& context) const; -}; - -} // BASE_HASH_NAMESPACE - -#endif // BASE_TRACE_EVENT_HEAP_PROFILER_ALLOCATION_CONTEXT_H_ diff --git a/base/trace_event/heap_profiler_allocation_context_tracker.cc b/base/trace_event/heap_profiler_allocation_context_tracker.cc deleted file mode 100644 index b47dc16edd..0000000000 --- a/base/trace_event/heap_profiler_allocation_context_tracker.cc +++ /dev/null @@ -1,250 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/trace_event/heap_profiler_allocation_context_tracker.h" - -#include <algorithm> -#include <iterator> - -#include "base/atomicops.h" -#include "base/debug/leak_annotations.h" -#include "base/threading/platform_thread.h" -#include "base/threading/thread_local_storage.h" -#include "base/trace_event/heap_profiler_allocation_context.h" - -#if defined(OS_LINUX) || defined(OS_ANDROID) -#include <sys/prctl.h> -#endif - -namespace base { -namespace trace_event { - -subtle::Atomic32 AllocationContextTracker::capture_mode_ = - static_cast<int32_t>(AllocationContextTracker::CaptureMode::DISABLED); - -namespace { - -const size_t kMaxStackDepth = 128u; -const size_t kMaxTaskDepth = 16u; -AllocationContextTracker* const kInitializingSentinel = - reinterpret_cast<AllocationContextTracker*>(-1); - -ThreadLocalStorage::StaticSlot g_tls_alloc_ctx_tracker = TLS_INITIALIZER; - -// This function is added to the TLS slot to clean up the instance when the -// thread exits. -void DestructAllocationContextTracker(void* alloc_ctx_tracker) { - delete static_cast<AllocationContextTracker*>(alloc_ctx_tracker); -} - -// Cannot call ThreadIdNameManager::GetName because it holds a lock and causes -// deadlock when lock is already held by ThreadIdNameManager before the current -// allocation. Gets the thread name from kernel if available or returns a string -// with id. This function intenionally leaks the allocated strings since they -// are used to tag allocations even after the thread dies. -const char* GetAndLeakThreadName() { - char name[16]; -#if defined(OS_LINUX) || defined(OS_ANDROID) - // If the thread name is not set, try to get it from prctl. Thread name might - // not be set in cases where the thread started before heap profiling was - // enabled. - int err = prctl(PR_GET_NAME, name); - if (!err) { - return strdup(name); - } -#endif // defined(OS_LINUX) || defined(OS_ANDROID) - - // Use tid if we don't have a thread name. - snprintf(name, sizeof(name), "%lu", - static_cast<unsigned long>(PlatformThread::CurrentId())); - return strdup(name); -} - -} // namespace - -// static -AllocationContextTracker* -AllocationContextTracker::GetInstanceForCurrentThread() { - AllocationContextTracker* tracker = - static_cast<AllocationContextTracker*>(g_tls_alloc_ctx_tracker.Get()); - if (tracker == kInitializingSentinel) - return nullptr; // Re-entrancy case. - - if (!tracker) { - g_tls_alloc_ctx_tracker.Set(kInitializingSentinel); - tracker = new AllocationContextTracker(); - g_tls_alloc_ctx_tracker.Set(tracker); - } - - return tracker; -} - -AllocationContextTracker::AllocationContextTracker() - : thread_name_(nullptr), ignore_scope_depth_(0) { - pseudo_stack_.reserve(kMaxStackDepth); - task_contexts_.reserve(kMaxTaskDepth); -} -AllocationContextTracker::~AllocationContextTracker() {} - -// static -void AllocationContextTracker::SetCurrentThreadName(const char* name) { - if (name && capture_mode() != CaptureMode::DISABLED) { - GetInstanceForCurrentThread()->thread_name_ = name; - } -} - -// static -void AllocationContextTracker::SetCaptureMode(CaptureMode mode) { - // When enabling capturing, also initialize the TLS slot. This does not create - // a TLS instance yet. - if (mode != CaptureMode::DISABLED && !g_tls_alloc_ctx_tracker.initialized()) - g_tls_alloc_ctx_tracker.Initialize(DestructAllocationContextTracker); - - // Release ordering ensures that when a thread observes |capture_mode_| to - // be true through an acquire load, the TLS slot has been initialized. - subtle::Release_Store(&capture_mode_, static_cast<int32_t>(mode)); -} - -void AllocationContextTracker::PushPseudoStackFrame( - AllocationContextTracker::PseudoStackFrame stack_frame) { - // Impose a limit on the height to verify that every push is popped, because - // in practice the pseudo stack never grows higher than ~20 frames. - if (pseudo_stack_.size() < kMaxStackDepth) - pseudo_stack_.push_back(stack_frame); - else - NOTREACHED(); -} - -void AllocationContextTracker::PopPseudoStackFrame( - AllocationContextTracker::PseudoStackFrame stack_frame) { - // Guard for stack underflow. If tracing was started with a TRACE_EVENT in - // scope, the frame was never pushed, so it is possible that pop is called - // on an empty stack. - if (pseudo_stack_.empty()) - return; - - // Assert that pushes and pops are nested correctly. This DCHECK can be - // hit if some TRACE_EVENT macro is unbalanced (a TRACE_EVENT_END* call - // without a corresponding TRACE_EVENT_BEGIN). - DCHECK(stack_frame == pseudo_stack_.back()) - << "Encountered an unmatched TRACE_EVENT_END: " - << stack_frame.trace_event_name - << " vs event in stack: " << pseudo_stack_.back().trace_event_name; - - pseudo_stack_.pop_back(); -} - -void AllocationContextTracker::PushCurrentTaskContext(const char* context) { - DCHECK(context); - if (task_contexts_.size() < kMaxTaskDepth) - task_contexts_.push_back(context); - else - NOTREACHED(); -} - -void AllocationContextTracker::PopCurrentTaskContext(const char* context) { - // Guard for stack underflow. If tracing was started with a TRACE_EVENT in - // scope, the context was never pushed, so it is possible that pop is called - // on an empty stack. - if (task_contexts_.empty()) - return; - - DCHECK_EQ(context, task_contexts_.back()) - << "Encountered an unmatched context end"; - task_contexts_.pop_back(); -} - -// static -bool AllocationContextTracker::GetContextSnapshot(AllocationContext* ctx) { - if (ignore_scope_depth_) - return false; - - CaptureMode mode = static_cast<CaptureMode>( - subtle::NoBarrier_Load(&capture_mode_)); - - auto* backtrace = std::begin(ctx->backtrace.frames); - auto* backtrace_end = std::end(ctx->backtrace.frames); - - if (!thread_name_) { - // Ignore the string allocation made by GetAndLeakThreadName to avoid - // reentrancy. - ignore_scope_depth_++; - thread_name_ = GetAndLeakThreadName(); - ANNOTATE_LEAKING_OBJECT_PTR(thread_name_); - DCHECK(thread_name_); - ignore_scope_depth_--; - } - - // Add the thread name as the first entry in pseudo stack. - if (thread_name_) { - *backtrace++ = StackFrame::FromThreadName(thread_name_); - } - - switch (mode) { - case CaptureMode::DISABLED: - { - break; - } - case CaptureMode::PSEUDO_STACK: - { - for (const PseudoStackFrame& stack_frame : pseudo_stack_) { - if (backtrace == backtrace_end) { - break; - } - *backtrace++ = - StackFrame::FromTraceEventName(stack_frame.trace_event_name); - } - break; - } - case CaptureMode::NATIVE_STACK: - { - // Backtrace contract requires us to return bottom frames, i.e. - // from main() and up. Stack unwinding produces top frames, i.e. - // from this point and up until main(). We request many frames to - // make sure we reach main(), and then copy bottom portion of them. - const void* frames[128]; - static_assert(arraysize(frames) >= Backtrace::kMaxFrameCount, - "not requesting enough frames to fill Backtrace"); -#if HAVE_TRACE_STACK_FRAME_POINTERS && !defined(OS_NACL) - size_t frame_count = debug::TraceStackFramePointers( - frames, - arraysize(frames), - 1 /* exclude this function from the trace */ ); -#else - size_t frame_count = 0; - NOTREACHED(); -#endif - - // Copy frames backwards - size_t backtrace_capacity = backtrace_end - backtrace; - int32_t top_frame_index = (backtrace_capacity >= frame_count) - ? 0 - : frame_count - backtrace_capacity; - for (int32_t i = frame_count - 1; i >= top_frame_index; --i) { - const void* frame = frames[i]; - *backtrace++ = StackFrame::FromProgramCounter(frame); - } - break; - } - } - - ctx->backtrace.frame_count = backtrace - std::begin(ctx->backtrace.frames); - - // TODO(ssid): Fix crbug.com/594803 to add file name as 3rd dimension - // (component name) in the heap profiler and not piggy back on the type name. - if (!task_contexts_.empty()) { - ctx->type_name = task_contexts_.back(); - } else if (!pseudo_stack_.empty()) { - // If task context was unavailable, then the category names are taken from - // trace events. - ctx->type_name = pseudo_stack_.back().trace_event_category; - } else { - ctx->type_name = nullptr; - } - - return true; -} - -} // namespace trace_event -} // namespace base diff --git a/base/trace_event/heap_profiler_allocation_context_tracker.h b/base/trace_event/heap_profiler_allocation_context_tracker.h deleted file mode 100644 index 4f2a8c9502..0000000000 --- a/base/trace_event/heap_profiler_allocation_context_tracker.h +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_TRACE_EVENT_HEAP_PROFILER_ALLOCATION_CONTEXT_TRACKER_H_ -#define BASE_TRACE_EVENT_HEAP_PROFILER_ALLOCATION_CONTEXT_TRACKER_H_ - -#include <vector> - -#include "base/atomicops.h" -#include "base/base_export.h" -#include "base/debug/stack_trace.h" -#include "base/macros.h" -#include "base/trace_event/heap_profiler_allocation_context.h" - -namespace base { -namespace trace_event { - -// The allocation context tracker keeps track of thread-local context for heap -// profiling. It includes a pseudo stack of trace events. On every allocation -// the tracker provides a snapshot of its context in the form of an -// |AllocationContext| that is to be stored together with the allocation -// details. -class BASE_EXPORT AllocationContextTracker { - public: - enum class CaptureMode: int32_t { - DISABLED, // Don't capture anything - PSEUDO_STACK, // GetContextSnapshot() returns pseudo stack trace - NATIVE_STACK // GetContextSnapshot() returns native (real) stack trace - }; - - // Stack frame constructed from trace events in codebase. - struct BASE_EXPORT PseudoStackFrame { - const char* trace_event_category; - const char* trace_event_name; - - bool operator==(const PseudoStackFrame& other) const { - return trace_event_category == other.trace_event_category && - trace_event_name == other.trace_event_name; - } - }; - - // Globally sets capturing mode. - // TODO(primiano): How to guard against *_STACK -> DISABLED -> *_STACK? - static void SetCaptureMode(CaptureMode mode); - - // Returns global capturing mode. - inline static CaptureMode capture_mode() { - // A little lag after heap profiling is enabled or disabled is fine, it is - // more important that the check is as cheap as possible when capturing is - // not enabled, so do not issue a memory barrier in the fast path. - if (subtle::NoBarrier_Load(&capture_mode_) == - static_cast<int32_t>(CaptureMode::DISABLED)) - return CaptureMode::DISABLED; - - // In the slow path, an acquire load is required to pair with the release - // store in |SetCaptureMode|. This is to ensure that the TLS slot for - // the thread-local allocation context tracker has been initialized if - // |capture_mode| returns something other than DISABLED. - return static_cast<CaptureMode>(subtle::Acquire_Load(&capture_mode_)); - } - - // Returns the thread-local instance, creating one if necessary. Returns - // always a valid instance, unless it is called re-entrantly, in which case - // returns nullptr in the nested calls. - static AllocationContextTracker* GetInstanceForCurrentThread(); - - // Set the thread name in the AllocationContextTracker of the current thread - // if capture is enabled. - static void SetCurrentThreadName(const char* name); - - // Starts and ends a new ignore scope between which the allocations are - // ignored by the heap profiler. GetContextSnapshot() returns false when - // allocations are ignored. - void begin_ignore_scope() { ignore_scope_depth_++; } - void end_ignore_scope() { - if (ignore_scope_depth_) - ignore_scope_depth_--; - } - - // Pushes a frame onto the thread-local pseudo stack. - void PushPseudoStackFrame(PseudoStackFrame stack_frame); - - // Pops a frame from the thread-local pseudo stack. - void PopPseudoStackFrame(PseudoStackFrame stack_frame); - - // Push and pop current task's context. A stack is used to support nested - // tasks and the top of the stack will be used in allocation context. - void PushCurrentTaskContext(const char* context); - void PopCurrentTaskContext(const char* context); - - // Fills a snapshot of the current thread-local context. Doesn't fill and - // returns false if allocations are being ignored. - bool GetContextSnapshot(AllocationContext* snapshot); - - ~AllocationContextTracker(); - - private: - AllocationContextTracker(); - - static subtle::Atomic32 capture_mode_; - - // The pseudo stack where frames are |TRACE_EVENT| names. - std::vector<PseudoStackFrame> pseudo_stack_; - - // The thread name is used as the first entry in the pseudo stack. - const char* thread_name_; - - // Stack of tasks' contexts. Context serves as a different dimension than - // pseudo stack to cluster allocations. - std::vector<const char*> task_contexts_; - - uint32_t ignore_scope_depth_; - - DISALLOW_COPY_AND_ASSIGN(AllocationContextTracker); -}; - -} // namespace trace_event -} // namespace base - -#endif // BASE_TRACE_EVENT_HEAP_PROFILER_ALLOCATION_CONTEXT_TRACKER_H_ diff --git a/base/trace_event/heap_profiler_allocation_context_tracker_unittest.cc b/base/trace_event/heap_profiler_allocation_context_tracker_unittest.cc deleted file mode 100644 index 6317886b0d..0000000000 --- a/base/trace_event/heap_profiler_allocation_context_tracker_unittest.cc +++ /dev/null @@ -1,321 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include <stddef.h> - -#include <iterator> - -#include "base/memory/ref_counted.h" -#include "base/pending_task.h" -#include "base/trace_event/heap_profiler.h" -#include "base/trace_event/heap_profiler_allocation_context.h" -#include "base/trace_event/heap_profiler_allocation_context_tracker.h" -#include "base/trace_event/memory_dump_manager.h" -#include "base/trace_event/trace_event.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { -namespace trace_event { - -// Define all strings once, because the pseudo stack requires pointer equality, -// and string interning is unreliable. -const char kThreadName[] = "TestThread"; -const char kCupcake[] = "Cupcake"; -const char kDonut[] = "Donut"; -const char kEclair[] = "Eclair"; -const char kFroyo[] = "Froyo"; -const char kGingerbread[] = "Gingerbread"; - -const char kFilteringTraceConfig[] = - "{" - " \"event_filters\": [" - " {" - " \"excluded_categories\": []," - " \"filter_args\": {}," - " \"filter_predicate\": \"heap_profiler_predicate\"," - " \"included_categories\": [" - " \"*\"," - " \"" TRACE_DISABLED_BY_DEFAULT("Testing") "\"]" - " }" - " ]" - "}"; - -// Asserts that the fixed-size array |expected_backtrace| matches the backtrace -// in |AllocationContextTracker::GetContextSnapshot|. -template <size_t N> -void AssertBacktraceEquals(const StackFrame(&expected_backtrace)[N]) { - AllocationContext ctx; - ASSERT_TRUE(AllocationContextTracker::GetInstanceForCurrentThread() - ->GetContextSnapshot(&ctx)); - - auto* actual = std::begin(ctx.backtrace.frames); - auto* actual_bottom = actual + ctx.backtrace.frame_count; - auto expected = std::begin(expected_backtrace); - auto expected_bottom = std::end(expected_backtrace); - - // Note that this requires the pointers to be equal, this is not doing a deep - // string comparison. - for (; actual != actual_bottom && expected != expected_bottom; - actual++, expected++) - ASSERT_EQ(*expected, *actual); - - // Ensure that the height of the stacks is the same. - ASSERT_EQ(actual, actual_bottom); - ASSERT_EQ(expected, expected_bottom); -} - -void AssertBacktraceContainsOnlyThreadName() { - StackFrame t = StackFrame::FromThreadName(kThreadName); - AllocationContext ctx; - ASSERT_TRUE(AllocationContextTracker::GetInstanceForCurrentThread() - ->GetContextSnapshot(&ctx)); - - ASSERT_EQ(1u, ctx.backtrace.frame_count); - ASSERT_EQ(t, ctx.backtrace.frames[0]); -} - -class AllocationContextTrackerTest : public testing::Test { - public: - void SetUp() override { - AllocationContextTracker::SetCaptureMode( - AllocationContextTracker::CaptureMode::PSEUDO_STACK); - // Enabling memory-infra category sets default memory dump config which - // includes filters for capturing pseudo stack. - TraceConfig config(kFilteringTraceConfig); - TraceLog::GetInstance()->SetEnabled(config, TraceLog::FILTERING_MODE); - AllocationContextTracker::SetCurrentThreadName(kThreadName); - } - - void TearDown() override { - AllocationContextTracker::SetCaptureMode( - AllocationContextTracker::CaptureMode::DISABLED); - TraceLog::GetInstance()->SetDisabled(TraceLog::FILTERING_MODE); - } -}; - -// Check that |TRACE_EVENT| macros push and pop to the pseudo stack correctly. -TEST_F(AllocationContextTrackerTest, PseudoStackScopedTrace) { - StackFrame t = StackFrame::FromThreadName(kThreadName); - StackFrame c = StackFrame::FromTraceEventName(kCupcake); - StackFrame d = StackFrame::FromTraceEventName(kDonut); - StackFrame e = StackFrame::FromTraceEventName(kEclair); - StackFrame f = StackFrame::FromTraceEventName(kFroyo); - - AssertBacktraceContainsOnlyThreadName(); - - { - TRACE_EVENT0("Testing", kCupcake); - StackFrame frame_c[] = {t, c}; - AssertBacktraceEquals(frame_c); - - { - TRACE_EVENT0("Testing", kDonut); - StackFrame frame_cd[] = {t, c, d}; - AssertBacktraceEquals(frame_cd); - } - - AssertBacktraceEquals(frame_c); - - { - TRACE_EVENT0("Testing", kEclair); - StackFrame frame_ce[] = {t, c, e}; - AssertBacktraceEquals(frame_ce); - } - - { - TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("NotTesting"), kDonut); - TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("Testing"), kCupcake); - StackFrame frame_cc[] = {t, c, c}; - AssertBacktraceEquals(frame_cc); - } - - AssertBacktraceEquals(frame_c); - } - - AssertBacktraceContainsOnlyThreadName(); - - { - TRACE_EVENT0("Testing", kFroyo); - StackFrame frame_f[] = {t, f}; - AssertBacktraceEquals(frame_f); - } - - AssertBacktraceContainsOnlyThreadName(); -} - -// Same as |PseudoStackScopedTrace|, but now test the |TRACE_EVENT_BEGIN| and -// |TRACE_EVENT_END| macros. -TEST_F(AllocationContextTrackerTest, PseudoStackBeginEndTrace) { - StackFrame t = StackFrame::FromThreadName(kThreadName); - StackFrame c = StackFrame::FromTraceEventName(kCupcake); - StackFrame d = StackFrame::FromTraceEventName(kDonut); - StackFrame e = StackFrame::FromTraceEventName(kEclair); - StackFrame f = StackFrame::FromTraceEventName(kFroyo); - - StackFrame frame_c[] = {t, c}; - StackFrame frame_cd[] = {t, c, d}; - StackFrame frame_ce[] = {t, c, e}; - StackFrame frame_f[] = {t, f}; - - AssertBacktraceContainsOnlyThreadName(); - - TRACE_EVENT_BEGIN0("Testing", kCupcake); - AssertBacktraceEquals(frame_c); - - TRACE_EVENT_BEGIN0("Testing", kDonut); - AssertBacktraceEquals(frame_cd); - TRACE_EVENT_END0("Testing", kDonut); - - AssertBacktraceEquals(frame_c); - - TRACE_EVENT_BEGIN0("Testing", kEclair); - AssertBacktraceEquals(frame_ce); - TRACE_EVENT_END0("Testing", kEclair); - - AssertBacktraceEquals(frame_c); - TRACE_EVENT_END0("Testing", kCupcake); - - AssertBacktraceContainsOnlyThreadName(); - - TRACE_EVENT_BEGIN0("Testing", kFroyo); - AssertBacktraceEquals(frame_f); - TRACE_EVENT_END0("Testing", kFroyo); - - AssertBacktraceContainsOnlyThreadName(); -} - -TEST_F(AllocationContextTrackerTest, PseudoStackMixedTrace) { - StackFrame t = StackFrame::FromThreadName(kThreadName); - StackFrame c = StackFrame::FromTraceEventName(kCupcake); - StackFrame d = StackFrame::FromTraceEventName(kDonut); - StackFrame e = StackFrame::FromTraceEventName(kEclair); - StackFrame f = StackFrame::FromTraceEventName(kFroyo); - - StackFrame frame_c[] = {t, c}; - StackFrame frame_cd[] = {t, c, d}; - StackFrame frame_e[] = {t, e}; - StackFrame frame_ef[] = {t, e, f}; - - AssertBacktraceContainsOnlyThreadName(); - - TRACE_EVENT_BEGIN0("Testing", kCupcake); - AssertBacktraceEquals(frame_c); - - { - TRACE_EVENT0("Testing", kDonut); - AssertBacktraceEquals(frame_cd); - } - - AssertBacktraceEquals(frame_c); - TRACE_EVENT_END0("Testing", kCupcake); - AssertBacktraceContainsOnlyThreadName(); - - { - TRACE_EVENT0("Testing", kEclair); - AssertBacktraceEquals(frame_e); - - TRACE_EVENT_BEGIN0("Testing", kFroyo); - AssertBacktraceEquals(frame_ef); - TRACE_EVENT_END0("Testing", kFroyo); - AssertBacktraceEquals(frame_e); - } - - AssertBacktraceContainsOnlyThreadName(); -} - -TEST_F(AllocationContextTrackerTest, BacktraceTakesTop) { - StackFrame t = StackFrame::FromThreadName(kThreadName); - StackFrame c = StackFrame::FromTraceEventName(kCupcake); - StackFrame f = StackFrame::FromTraceEventName(kFroyo); - - // Push 11 events onto the pseudo stack. - TRACE_EVENT0("Testing", kCupcake); - TRACE_EVENT0("Testing", kCupcake); - TRACE_EVENT0("Testing", kCupcake); - - TRACE_EVENT0("Testing", kCupcake); - TRACE_EVENT0("Testing", kCupcake); - TRACE_EVENT0("Testing", kCupcake); - TRACE_EVENT0("Testing", kCupcake); - - TRACE_EVENT0("Testing", kCupcake); - TRACE_EVENT0("Testing", kDonut); - TRACE_EVENT0("Testing", kEclair); - TRACE_EVENT0("Testing", kFroyo); - - { - TRACE_EVENT0("Testing", kGingerbread); - AllocationContext ctx; - ASSERT_TRUE(AllocationContextTracker::GetInstanceForCurrentThread() - ->GetContextSnapshot(&ctx)); - - // The pseudo stack relies on pointer equality, not deep string comparisons. - ASSERT_EQ(t, ctx.backtrace.frames[0]); - ASSERT_EQ(c, ctx.backtrace.frames[1]); - ASSERT_EQ(f, ctx.backtrace.frames[11]); - } - - { - AllocationContext ctx; - ASSERT_TRUE(AllocationContextTracker::GetInstanceForCurrentThread() - ->GetContextSnapshot(&ctx)); - ASSERT_EQ(t, ctx.backtrace.frames[0]); - ASSERT_EQ(c, ctx.backtrace.frames[1]); - ASSERT_EQ(f, ctx.backtrace.frames[11]); - } -} - -TEST_F(AllocationContextTrackerTest, TrackCategoryName) { - const char kContext1[] = "context1"; - const char kContext2[] = "context2"; - { - // The context from the scoped task event should be used as type name. - TRACE_HEAP_PROFILER_API_SCOPED_TASK_EXECUTION event1(kContext1); - AllocationContext ctx1; - ASSERT_TRUE(AllocationContextTracker::GetInstanceForCurrentThread() - ->GetContextSnapshot(&ctx1)); - ASSERT_EQ(kContext1, ctx1.type_name); - - // In case of nested events, the last event's context should be used. - TRACE_HEAP_PROFILER_API_SCOPED_TASK_EXECUTION event2(kContext2); - AllocationContext ctx2; - ASSERT_TRUE(AllocationContextTracker::GetInstanceForCurrentThread() - ->GetContextSnapshot(&ctx2)); - ASSERT_EQ(kContext2, ctx2.type_name); - } - - { - // Type should be category name of the last seen trace event. - TRACE_EVENT0("Testing", kCupcake); - AllocationContext ctx1; - ASSERT_TRUE(AllocationContextTracker::GetInstanceForCurrentThread() - ->GetContextSnapshot(&ctx1)); - ASSERT_EQ("Testing", std::string(ctx1.type_name)); - - TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("Testing"), kCupcake); - AllocationContext ctx2; - ASSERT_TRUE(AllocationContextTracker::GetInstanceForCurrentThread() - ->GetContextSnapshot(&ctx2)); - ASSERT_EQ(TRACE_DISABLED_BY_DEFAULT("Testing"), - std::string(ctx2.type_name)); - } - - // Type should be nullptr without task event. - AllocationContext ctx; - ASSERT_TRUE(AllocationContextTracker::GetInstanceForCurrentThread() - ->GetContextSnapshot(&ctx)); - ASSERT_FALSE(ctx.type_name); -} - -TEST_F(AllocationContextTrackerTest, IgnoreAllocationTest) { - TRACE_EVENT0("Testing", kCupcake); - TRACE_EVENT0("Testing", kDonut); - HEAP_PROFILER_SCOPED_IGNORE; - AllocationContext ctx; - ASSERT_FALSE(AllocationContextTracker::GetInstanceForCurrentThread() - ->GetContextSnapshot(&ctx)); -} - -} // namespace trace_event -} // namespace base diff --git a/base/trace_event/heap_profiler_allocation_register.cc b/base/trace_event/heap_profiler_allocation_register.cc deleted file mode 100644 index b9f440adb6..0000000000 --- a/base/trace_event/heap_profiler_allocation_register.cc +++ /dev/null @@ -1,194 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/trace_event/heap_profiler_allocation_register.h" - -#include <algorithm> -#include <limits> - -#include "base/trace_event/trace_event_memory_overhead.h" - -namespace base { -namespace trace_event { - -AllocationRegister::ConstIterator::ConstIterator( - const AllocationRegister& alloc_register, - AllocationIndex index) - : register_(alloc_register), index_(index) {} - -void AllocationRegister::ConstIterator::operator++() { - index_ = register_.allocations_.Next(index_ + 1); -} - -bool AllocationRegister::ConstIterator::operator!=( - const ConstIterator& other) const { - return index_ != other.index_; -} - -AllocationRegister::Allocation AllocationRegister::ConstIterator::operator*() - const { - return register_.GetAllocation(index_); -} - -size_t AllocationRegister::BacktraceHasher::operator()( - const Backtrace& backtrace) const { - const size_t kSampleLength = 10; - - uintptr_t total_value = 0; - - size_t head_end = std::min(backtrace.frame_count, kSampleLength); - for (size_t i = 0; i != head_end; ++i) { - total_value += reinterpret_cast<uintptr_t>(backtrace.frames[i].value); - } - - size_t tail_start = backtrace.frame_count - - std::min(backtrace.frame_count - head_end, kSampleLength); - for (size_t i = tail_start; i != backtrace.frame_count; ++i) { - total_value += reinterpret_cast<uintptr_t>(backtrace.frames[i].value); - } - - total_value += backtrace.frame_count; - - // These magic constants give best results in terms of average collisions - // per backtrace. They were found by replaying real backtraces from Linux - // and Android against different hash functions. - return (total_value * 131101) >> 14; -} - -size_t AllocationRegister::AddressHasher::operator()( - const void* address) const { - // The multiplicative hashing scheme from [Knuth 1998]. The value of |a| has - // been chosen carefully based on measurements with real-word data (addresses - // recorded from a Chrome trace run). It is the first prime after 2^17. For - // |shift|, 15 yield good results for both 2^18 and 2^19 bucket sizes. - // Microbenchmarks show that this simple scheme outperforms fancy hashes like - // Murmur3 by 20 to 40 percent. - const uintptr_t key = reinterpret_cast<uintptr_t>(address); - const uintptr_t a = 131101; - const uintptr_t shift = 15; - const uintptr_t h = (key * a) >> shift; - return h; -} - -AllocationRegister::AllocationRegister() - : AllocationRegister(kAllocationCapacity, kBacktraceCapacity) {} - -AllocationRegister::AllocationRegister(size_t allocation_capacity, - size_t backtrace_capacity) - : allocations_(allocation_capacity), backtraces_(backtrace_capacity) { - Backtrace sentinel = {}; - sentinel.frames[0] = StackFrame::FromThreadName("[out of heap profiler mem]"); - sentinel.frame_count = 1; - - // Rationale for max / 2: in theory we could just start the sentinel with a - // refcount == 0. However, using max / 2 allows short circuiting of the - // conditional in RemoveBacktrace() keeping the sentinel logic out of the fast - // path. From a functional viewpoint, the sentinel is safe even if we wrap - // over refcount because . - BacktraceMap::KVPair::second_type sentinel_refcount = - std::numeric_limits<BacktraceMap::KVPair::second_type>::max() / 2; - auto index_and_flag = backtraces_.Insert(sentinel, sentinel_refcount); - DCHECK(index_and_flag.second); - DCHECK_EQ(index_and_flag.first, kOutOfStorageBacktraceIndex); -} - -AllocationRegister::~AllocationRegister() {} - -bool AllocationRegister::Insert(const void* address, - size_t size, - const AllocationContext& context) { - DCHECK(address != nullptr); - if (size == 0) { - return false; - } - - AllocationInfo info = {size, context.type_name, - InsertBacktrace(context.backtrace)}; - - // Try to insert the allocation. - auto index_and_flag = allocations_.Insert(address, info); - if (!index_and_flag.second && - index_and_flag.first != AllocationMap::kInvalidKVIndex) { - // |address| is already there - overwrite the allocation info. - auto& old_info = allocations_.Get(index_and_flag.first).second; - RemoveBacktrace(old_info.backtrace_index); - old_info = info; - return true; - } - - return index_and_flag.second; -} - -void AllocationRegister::Remove(const void* address) { - auto index = allocations_.Find(address); - if (index == AllocationMap::kInvalidKVIndex) { - return; - } - - const AllocationInfo& info = allocations_.Get(index).second; - RemoveBacktrace(info.backtrace_index); - allocations_.Remove(index); -} - -bool AllocationRegister::Get(const void* address, - Allocation* out_allocation) const { - auto index = allocations_.Find(address); - if (index == AllocationMap::kInvalidKVIndex) { - return false; - } - - if (out_allocation) { - *out_allocation = GetAllocation(index); - } - return true; -} - -AllocationRegister::ConstIterator AllocationRegister::begin() const { - return ConstIterator(*this, allocations_.Next(0)); -} - -AllocationRegister::ConstIterator AllocationRegister::end() const { - return ConstIterator(*this, AllocationMap::kInvalidKVIndex); -} - -void AllocationRegister::EstimateTraceMemoryOverhead( - TraceEventMemoryOverhead* overhead) const { - size_t allocated = sizeof(AllocationRegister); - size_t resident = sizeof(AllocationRegister) + - allocations_.EstimateUsedMemory() + - backtraces_.EstimateUsedMemory(); - overhead->Add("AllocationRegister", allocated, resident); -} - -AllocationRegister::BacktraceMap::KVIndex AllocationRegister::InsertBacktrace( - const Backtrace& backtrace) { - auto index = backtraces_.Insert(backtrace, 0).first; - if (index == BacktraceMap::kInvalidKVIndex) - return kOutOfStorageBacktraceIndex; - auto& backtrace_and_count = backtraces_.Get(index); - backtrace_and_count.second++; - return index; -} - -void AllocationRegister::RemoveBacktrace(BacktraceMap::KVIndex index) { - auto& backtrace_and_count = backtraces_.Get(index); - if (--backtrace_and_count.second == 0 && - index != kOutOfStorageBacktraceIndex) { - // Backtrace is not referenced anymore - remove it. - backtraces_.Remove(index); - } -} - -AllocationRegister::Allocation AllocationRegister::GetAllocation( - AllocationMap::KVIndex index) const { - const auto& address_and_info = allocations_.Get(index); - const auto& backtrace_and_count = - backtraces_.Get(address_and_info.second.backtrace_index); - return {address_and_info.first, address_and_info.second.size, - AllocationContext(backtrace_and_count.first, - address_and_info.second.type_name)}; -} - -} // namespace trace_event -} // namespace base diff --git a/base/trace_event/heap_profiler_allocation_register.h b/base/trace_event/heap_profiler_allocation_register.h deleted file mode 100644 index ac9872f001..0000000000 --- a/base/trace_event/heap_profiler_allocation_register.h +++ /dev/null @@ -1,385 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_TRACE_EVENT_HEAP_PROFILER_ALLOCATION_REGISTER_H_ -#define BASE_TRACE_EVENT_HEAP_PROFILER_ALLOCATION_REGISTER_H_ - -#include <stddef.h> -#include <stdint.h> - -#include <utility> - -#include "base/bits.h" -#include "base/logging.h" -#include "base/macros.h" -#include "base/process/process_metrics.h" -#include "base/template_util.h" -#include "base/trace_event/heap_profiler_allocation_context.h" -#include "build/build_config.h" - -namespace base { -namespace trace_event { - -class AllocationRegisterTest; - -namespace internal { - -// Allocates a region of virtual address space of |size| rounded up to the -// system page size. The memory is zeroed by the system. A guard page is -// added after the end. -void* AllocateGuardedVirtualMemory(size_t size); - -// Frees a region of virtual address space allocated by a call to -// |AllocateVirtualMemory|. -void FreeGuardedVirtualMemory(void* address, size_t allocated_size); - -// Hash map that mmaps memory only once in the constructor. Its API is -// similar to std::unordered_map, only index (KVIndex) is used to address -template <size_t NumBuckets, class Key, class Value, class KeyHasher> -class FixedHashMap { - // To keep things simple we don't call destructors. - static_assert(is_trivially_destructible<Key>::value && - is_trivially_destructible<Value>::value, - "Key and Value shouldn't have destructors"); - public: - using KVPair = std::pair<const Key, Value>; - - // For implementation simplicity API uses integer index instead - // of iterators. Most operations (except Find) on KVIndex are O(1). - using KVIndex = size_t; - enum : KVIndex { kInvalidKVIndex = static_cast<KVIndex>(-1) }; - - // Capacity controls how many items this hash map can hold, and largely - // affects memory footprint. - explicit FixedHashMap(size_t capacity) - : num_cells_(capacity), - num_inserts_dropped_(0), - cells_(static_cast<Cell*>( - AllocateGuardedVirtualMemory(num_cells_ * sizeof(Cell)))), - buckets_(static_cast<Bucket*>( - AllocateGuardedVirtualMemory(NumBuckets * sizeof(Bucket)))), - free_list_(nullptr), - next_unused_cell_(0) {} - - ~FixedHashMap() { - FreeGuardedVirtualMemory(cells_, num_cells_ * sizeof(Cell)); - FreeGuardedVirtualMemory(buckets_, NumBuckets * sizeof(Bucket)); - } - - // Returns {kInvalidKVIndex, false} if the table is full. - std::pair<KVIndex, bool> Insert(const Key& key, const Value& value) { - Cell** p_cell = Lookup(key); - Cell* cell = *p_cell; - if (cell) { - return {static_cast<KVIndex>(cell - cells_), false}; // not inserted - } - - // Get a free cell and link it. - cell = GetFreeCell(); - if (!cell) { - if (num_inserts_dropped_ < - std::numeric_limits<decltype(num_inserts_dropped_)>::max()) { - ++num_inserts_dropped_; - } - return {kInvalidKVIndex, false}; - } - *p_cell = cell; - cell->p_prev = p_cell; - cell->next = nullptr; - - // Initialize key/value pair. Since key is 'const Key' this is the - // only way to initialize it. - new (&cell->kv) KVPair(key, value); - - return {static_cast<KVIndex>(cell - cells_), true}; // inserted - } - - void Remove(KVIndex index) { - DCHECK_LT(index, next_unused_cell_); - - Cell* cell = &cells_[index]; - - // Unlink the cell. - *cell->p_prev = cell->next; - if (cell->next) { - cell->next->p_prev = cell->p_prev; - } - cell->p_prev = nullptr; // mark as free - - // Add it to the free list. - cell->next = free_list_; - free_list_ = cell; - } - - KVIndex Find(const Key& key) const { - Cell* cell = *Lookup(key); - return cell ? static_cast<KVIndex>(cell - cells_) : kInvalidKVIndex; - } - - KVPair& Get(KVIndex index) { - return cells_[index].kv; - } - - const KVPair& Get(KVIndex index) const { - return cells_[index].kv; - } - - // Finds next index that has a KVPair associated with it. Search starts - // with the specified index. Returns kInvalidKVIndex if nothing was found. - // To find the first valid index, call this function with 0. Continue - // calling with the last_index + 1 until kInvalidKVIndex is returned. - KVIndex Next(KVIndex index) const { - for (;index < next_unused_cell_; ++index) { - if (cells_[index].p_prev) { - return index; - } - } - return kInvalidKVIndex; - } - - // Estimates number of bytes used in allocated memory regions. - size_t EstimateUsedMemory() const { - size_t page_size = base::GetPageSize(); - // |next_unused_cell_| is the first cell that wasn't touched, i.e. - // it's the number of touched cells. - return bits::Align(sizeof(Cell) * next_unused_cell_, page_size) + - bits::Align(sizeof(Bucket) * NumBuckets, page_size); - } - - size_t num_inserts_dropped() const { return num_inserts_dropped_; } - - private: - friend base::trace_event::AllocationRegisterTest; - - struct Cell { - KVPair kv; - Cell* next; - - // Conceptually this is |prev| in a doubly linked list. However, buckets - // also participate in the bucket's cell list - they point to the list's - // head and also need to be linked / unlinked properly. To treat these two - // cases uniformly, instead of |prev| we're storing "pointer to a Cell* - // that points to this Cell" kind of thing. So |p_prev| points to a bucket - // for the first cell in a list, and points to |next| of the previous cell - // for any other cell. With that Lookup() is the only function that handles - // buckets / cells differently. - // If |p_prev| is nullptr, the cell is in the free list. - Cell** p_prev; - }; - - using Bucket = Cell*; - - // Returns a pointer to the cell that contains or should contain the entry - // for |key|. The pointer may point at an element of |buckets_| or at the - // |next| member of an element of |cells_|. - Cell** Lookup(const Key& key) const { - // The list head is in |buckets_| at the hash offset. - Cell** p_cell = &buckets_[Hash(key)]; - - // Chase down the list until the cell that holds |key| is found, - // or until the list ends. - while (*p_cell && (*p_cell)->kv.first != key) { - p_cell = &(*p_cell)->next; - } - - return p_cell; - } - - // Returns a cell that is not being used to store an entry (either by - // recycling from the free list or by taking a fresh cell). May return - // nullptr if the hash table has run out of memory. - Cell* GetFreeCell() { - // First try to re-use a cell from the free list. - if (free_list_) { - Cell* cell = free_list_; - free_list_ = cell->next; - return cell; - } - - // If the hash table has too little capacity (when too little address space - // was reserved for |cells_|), return nullptr. - if (next_unused_cell_ >= num_cells_) { - return nullptr; - } - - // Otherwise pick the next cell that has not been touched before. - return &cells_[next_unused_cell_++]; - } - - // Returns a value in the range [0, NumBuckets - 1] (inclusive). - size_t Hash(const Key& key) const { - if (NumBuckets == (NumBuckets & ~(NumBuckets - 1))) { - // NumBuckets is a power of 2. - return KeyHasher()(key) & (NumBuckets - 1); - } else { - return KeyHasher()(key) % NumBuckets; - } - } - - // Number of cells. - size_t const num_cells_; - - // Number of calls to Insert() that were lost because the hashtable was full. - size_t num_inserts_dropped_; - - // The array of cells. This array is backed by mmapped memory. Lower indices - // are accessed first, higher indices are accessed only when the |free_list_| - // is empty. This is to minimize the amount of resident memory used. - Cell* const cells_; - - // The array of buckets (pointers into |cells_|). |buckets_[Hash(key)]| will - // contain the pointer to the linked list of cells for |Hash(key)|. - // This array is backed by mmapped memory. - mutable Bucket* buckets_; - - // The head of the free list. - Cell* free_list_; - - // The index of the first element of |cells_| that has not been used before. - // If the free list is empty and a new cell is needed, the cell at this index - // is used. This is the high water mark for the number of entries stored. - size_t next_unused_cell_; - - DISALLOW_COPY_AND_ASSIGN(FixedHashMap); -}; - -} // namespace internal - -class TraceEventMemoryOverhead; - -// The allocation register keeps track of all allocations that have not been -// freed. Internally it has two hashtables: one for Backtraces and one for -// actual allocations. Sizes of both hashtables are fixed, and this class -// allocates (mmaps) only in its constructor. -// -// When either hash table hits max size, new inserts are dropped. -class BASE_EXPORT AllocationRegister { - public: - // Details about an allocation. - struct Allocation { - const void* address; - size_t size; - AllocationContext context; - }; - - // An iterator that iterates entries in no particular order. - class BASE_EXPORT ConstIterator { - public: - void operator++(); - bool operator!=(const ConstIterator& other) const; - Allocation operator*() const; - - private: - friend class AllocationRegister; - using AllocationIndex = size_t; - - ConstIterator(const AllocationRegister& alloc_register, - AllocationIndex index); - - const AllocationRegister& register_; - AllocationIndex index_; - }; - - AllocationRegister(); - AllocationRegister(size_t allocation_capacity, size_t backtrace_capacity); - - ~AllocationRegister(); - - // Inserts allocation details into the table. If the address was present - // already, its details are updated. |address| must not be null. - // - // Returns true if an insert occurred. Inserts may fail because the table - // is full. - bool Insert(const void* address, - size_t size, - const AllocationContext& context); - - // Removes the address from the table if it is present. It is ok to call this - // with a null pointer. - void Remove(const void* address); - - // Finds allocation for the address and fills |out_allocation|. - bool Get(const void* address, Allocation* out_allocation) const; - - ConstIterator begin() const; - ConstIterator end() const; - - // Estimates memory overhead including |sizeof(AllocationRegister)|. - void EstimateTraceMemoryOverhead(TraceEventMemoryOverhead* overhead) const; - - private: - friend AllocationRegisterTest; - -// Expect lower number of allocations from mobile platforms. Load factor -// (capacity / bucket count) is kept less than 10 for optimal hashing. The -// number of buckets should be changed together with AddressHasher. -#if defined(OS_ANDROID) || defined(OS_IOS) - static const size_t kAllocationBuckets = 1 << 18; - static const size_t kAllocationCapacity = 1500000; -#else - static const size_t kAllocationBuckets = 1 << 19; - static const size_t kAllocationCapacity = 5000000; -#endif - - // 2^16 works well with BacktraceHasher. When increasing this number make - // sure BacktraceHasher still produces low number of collisions. - static const size_t kBacktraceBuckets = 1 << 16; -#if defined(OS_ANDROID) - static const size_t kBacktraceCapacity = 32000; // 22K was observed -#else - static const size_t kBacktraceCapacity = 55000; // 45K was observed on Linux -#endif - - struct BacktraceHasher { - size_t operator () (const Backtrace& backtrace) const; - }; - - using BacktraceMap = internal::FixedHashMap< - kBacktraceBuckets, - Backtrace, - size_t, // Number of references to the backtrace (the key). Incremented - // when an allocation that references the backtrace is inserted, - // and decremented when the allocation is removed. When the - // number drops to zero, the backtrace is removed from the map. - BacktraceHasher>; - - struct AllocationInfo { - size_t size; - const char* type_name; - BacktraceMap::KVIndex backtrace_index; - }; - - struct AddressHasher { - size_t operator () (const void* address) const; - }; - - using AllocationMap = internal::FixedHashMap< - kAllocationBuckets, - const void*, - AllocationInfo, - AddressHasher>; - - BacktraceMap::KVIndex InsertBacktrace(const Backtrace& backtrace); - void RemoveBacktrace(BacktraceMap::KVIndex index); - - Allocation GetAllocation(AllocationMap::KVIndex) const; - - AllocationMap allocations_; - BacktraceMap backtraces_; - - // Sentinel used when the |backtraces_| table is full. - // - // This is a slightly abstraction to allow for constant propagation. It - // knows that the sentinel will be the first item inserted into the table - // and that the first index retuned will be 0. The constructor DCHECKs - // this assumption. - enum : BacktraceMap::KVIndex { kOutOfStorageBacktraceIndex = 0 }; - - DISALLOW_COPY_AND_ASSIGN(AllocationRegister); -}; - -} // namespace trace_event -} // namespace base - -#endif // BASE_TRACE_EVENT_HEAP_PROFILER_ALLOCATION_REGISTER_H_ diff --git a/base/trace_event/heap_profiler_allocation_register_posix.cc b/base/trace_event/heap_profiler_allocation_register_posix.cc deleted file mode 100644 index 94eeb4df88..0000000000 --- a/base/trace_event/heap_profiler_allocation_register_posix.cc +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/trace_event/heap_profiler_allocation_register.h" - -#include <stddef.h> -#include <sys/mman.h> -#include <unistd.h> - -#include "base/bits.h" -#include "base/logging.h" -#include "base/process/process_metrics.h" - -#ifndef MAP_ANONYMOUS -#define MAP_ANONYMOUS MAP_ANON -#endif - -namespace base { -namespace trace_event { -namespace internal { - -namespace { -size_t GetGuardSize() { - return GetPageSize(); -} -} - -void* AllocateGuardedVirtualMemory(size_t size) { - size = bits::Align(size, GetPageSize()); - - // Add space for a guard page at the end. - size_t map_size = size + GetGuardSize(); - - void* addr = mmap(nullptr, map_size, PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - - PCHECK(addr != MAP_FAILED); - - // Mark the last page of the allocated address space as inaccessible - // (PROT_NONE). The read/write accessible space is still at least |min_size| - // bytes. - void* guard_addr = - reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(addr) + size); - int result = mprotect(guard_addr, GetGuardSize(), PROT_NONE); - PCHECK(result == 0); - - return addr; -} - -void FreeGuardedVirtualMemory(void* address, size_t allocated_size) { - size_t size = bits::Align(allocated_size, GetPageSize()) + GetGuardSize(); - munmap(address, size); -} - -} // namespace internal -} // namespace trace_event -} // namespace base diff --git a/base/trace_event/heap_profiler_event_filter.cc b/base/trace_event/heap_profiler_event_filter.cc deleted file mode 100644 index 6c91c91b13..0000000000 --- a/base/trace_event/heap_profiler_event_filter.cc +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/trace_event/heap_profiler_event_filter.h" - -#include "base/trace_event/category_registry.h" -#include "base/trace_event/heap_profiler_allocation_context_tracker.h" -#include "base/trace_event/trace_category.h" -#include "base/trace_event/trace_event.h" -#include "base/trace_event/trace_event_impl.h" - -namespace base { -namespace trace_event { - -namespace { - -inline bool IsPseudoStackEnabled() { - return AllocationContextTracker::capture_mode() == - AllocationContextTracker::CaptureMode::PSEUDO_STACK; -} - -inline AllocationContextTracker* GetThreadLocalTracker() { - return AllocationContextTracker::GetInstanceForCurrentThread(); -} - -} // namespace - -// static -const char HeapProfilerEventFilter::kName[] = "heap_profiler_predicate"; - -HeapProfilerEventFilter::HeapProfilerEventFilter() {} -HeapProfilerEventFilter::~HeapProfilerEventFilter() {} - -bool HeapProfilerEventFilter::FilterTraceEvent( - const TraceEvent& trace_event) const { - if (!IsPseudoStackEnabled()) - return true; - - // TODO(primiano): Add support for events with copied name crbug.com/581079. - if (trace_event.flags() & TRACE_EVENT_FLAG_COPY) - return true; - - const auto* category = CategoryRegistry::GetCategoryByStatePtr( - trace_event.category_group_enabled()); - AllocationContextTracker::PseudoStackFrame frame = {category->name(), - trace_event.name()}; - if (trace_event.phase() == TRACE_EVENT_PHASE_BEGIN || - trace_event.phase() == TRACE_EVENT_PHASE_COMPLETE) { - GetThreadLocalTracker()->PushPseudoStackFrame(frame); - } else if (trace_event.phase() == TRACE_EVENT_PHASE_END) { - // The pop for |TRACE_EVENT_PHASE_COMPLETE| events is in |EndEvent|. - GetThreadLocalTracker()->PopPseudoStackFrame(frame); - } - // Do not filter-out any events and always return true. TraceLog adds the - // event only if it is enabled for recording. - return true; -} - -void HeapProfilerEventFilter::EndEvent(const char* category_name, - const char* event_name) const { - if (IsPseudoStackEnabled()) - GetThreadLocalTracker()->PopPseudoStackFrame({category_name, event_name}); -} - -} // namespace trace_event -} // namespace base diff --git a/base/trace_event/heap_profiler_event_filter.h b/base/trace_event/heap_profiler_event_filter.h deleted file mode 100644 index 47368a1b07..0000000000 --- a/base/trace_event/heap_profiler_event_filter.h +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_TRACE_EVENT_HEAP_PROFILER_EVENT_FILTER_H_ -#define BASE_TRACE_EVENT_HEAP_PROFILER_EVENT_FILTER_H_ - -#include "base/base_export.h" -#include "base/macros.h" -#include "base/trace_event/trace_event_filter.h" - -namespace base { -namespace trace_event { - -class TraceEvent; - -// This filter unconditionally accepts all events and pushes/pops them from the -// thread-local AllocationContextTracker instance as they are seen. -// This is used to cheaply construct the heap profiler pseudo stack without -// having to actually record all events. -class BASE_EXPORT HeapProfilerEventFilter : public TraceEventFilter { - public: - static const char kName[]; - - HeapProfilerEventFilter(); - ~HeapProfilerEventFilter() override; - - // TraceEventFilter implementation. - bool FilterTraceEvent(const TraceEvent& trace_event) const override; - void EndEvent(const char* category_name, - const char* event_name) const override; - - private: - DISALLOW_COPY_AND_ASSIGN(HeapProfilerEventFilter); -}; - -} // namespace trace_event -} // namespace base - -#endif // BASE_TRACE_EVENT_HEAP_PROFILER_EVENT_FILTER_H_ diff --git a/base/trace_event/heap_profiler_heap_dump_writer.cc b/base/trace_event/heap_profiler_heap_dump_writer.cc deleted file mode 100644 index 8043fff995..0000000000 --- a/base/trace_event/heap_profiler_heap_dump_writer.cc +++ /dev/null @@ -1,322 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/trace_event/heap_profiler_heap_dump_writer.h" - -#include <stdint.h> - -#include <algorithm> -#include <iterator> -#include <tuple> -#include <utility> -#include <vector> - -#include "base/format_macros.h" -#include "base/logging.h" -#include "base/macros.h" -#include "base/strings/stringprintf.h" -#include "base/trace_event/heap_profiler_stack_frame_deduplicator.h" -#include "base/trace_event/heap_profiler_type_name_deduplicator.h" -#include "base/trace_event/memory_dump_session_state.h" -#include "base/trace_event/trace_config.h" -#include "base/trace_event/trace_event_argument.h" -#include "base/trace_event/trace_log.h" - -// Most of what the |HeapDumpWriter| does is aggregating detailed information -// about the heap and deciding what to dump. The Input to this process is a list -// of |AllocationContext|s and size pairs. -// -// The pairs are grouped into |Bucket|s. A bucket is a group of (context, size) -// pairs where the properties of the contexts share a prefix. (Type name is -// considered a list of length one here.) First all pairs are put into one -// bucket that represents the entire heap. Then this bucket is recursively -// broken down into smaller buckets. Each bucket keeps track of whether further -// breakdown is possible. - -namespace base { -namespace trace_event { -namespace internal { -namespace { - -// Denotes a property of |AllocationContext| to break down by. -enum class BreakDownMode { kByBacktrace, kByTypeName }; - -// A group of bytes for which the context shares a prefix. -struct Bucket { - Bucket() - : size(0), - count(0), - backtrace_cursor(0), - is_broken_down_by_type_name(false) {} - - std::vector<std::pair<const AllocationContext*, AllocationMetrics>> - metrics_by_context; - - // The sum of the sizes of |metrics_by_context|. - size_t size; - - // The sum of number of allocations of |metrics_by_context|. - size_t count; - - // The index of the stack frame that has not yet been broken down by. For all - // elements in this bucket, the stack frames 0 up to (but not including) the - // cursor, must be equal. - size_t backtrace_cursor; - - // When true, the type name for all elements in this bucket must be equal. - bool is_broken_down_by_type_name; -}; - -// Comparison operator to order buckets by their size. -bool operator<(const Bucket& lhs, const Bucket& rhs) { - return lhs.size < rhs.size; -} - -// Groups the allocations in the bucket by |break_by|. The buckets in the -// returned list will have |backtrace_cursor| advanced or -// |is_broken_down_by_type_name| set depending on the property to group by. -std::vector<Bucket> GetSubbuckets(const Bucket& bucket, - BreakDownMode break_by) { - base::hash_map<const void*, Bucket> breakdown; - - - if (break_by == BreakDownMode::kByBacktrace) { - for (const auto& context_and_metrics : bucket.metrics_by_context) { - const Backtrace& backtrace = context_and_metrics.first->backtrace; - const StackFrame* begin = std::begin(backtrace.frames); - const StackFrame* end = begin + backtrace.frame_count; - const StackFrame* cursor = begin + bucket.backtrace_cursor; - - DCHECK_LE(cursor, end); - - if (cursor != end) { - Bucket& subbucket = breakdown[cursor->value]; - subbucket.size += context_and_metrics.second.size; - subbucket.count += context_and_metrics.second.count; - subbucket.metrics_by_context.push_back(context_and_metrics); - subbucket.backtrace_cursor = bucket.backtrace_cursor + 1; - subbucket.is_broken_down_by_type_name = - bucket.is_broken_down_by_type_name; - DCHECK_GT(subbucket.size, 0u); - DCHECK_GT(subbucket.count, 0u); - } - } - } else if (break_by == BreakDownMode::kByTypeName) { - if (!bucket.is_broken_down_by_type_name) { - for (const auto& context_and_metrics : bucket.metrics_by_context) { - const AllocationContext* context = context_and_metrics.first; - Bucket& subbucket = breakdown[context->type_name]; - subbucket.size += context_and_metrics.second.size; - subbucket.count += context_and_metrics.second.count; - subbucket.metrics_by_context.push_back(context_and_metrics); - subbucket.backtrace_cursor = bucket.backtrace_cursor; - subbucket.is_broken_down_by_type_name = true; - DCHECK_GT(subbucket.size, 0u); - DCHECK_GT(subbucket.count, 0u); - } - } - } - - std::vector<Bucket> buckets; - buckets.reserve(breakdown.size()); - for (auto key_bucket : breakdown) - buckets.push_back(key_bucket.second); - - return buckets; -} - -// Breaks down the bucket by |break_by|. Returns only buckets that contribute -// more than |min_size_bytes| to the total size. The long tail is omitted. -std::vector<Bucket> BreakDownBy(const Bucket& bucket, - BreakDownMode break_by, - size_t min_size_bytes) { - std::vector<Bucket> buckets = GetSubbuckets(bucket, break_by); - - // Ensure that |buckets| is a max-heap (the data structure, not memory heap), - // so its front contains the largest bucket. Buckets should be iterated - // ordered by size, but sorting the vector is overkill because the long tail - // of small buckets will be discarded. By using a max-heap, the optimal case - // where all but the first bucket are discarded is O(n). The worst case where - // no bucket is discarded is doing a heap sort, which is O(n log n). - std::make_heap(buckets.begin(), buckets.end()); - - // Keep including buckets until adding one would increase the number of - // bytes accounted for by |min_size_bytes|. The large buckets end up in - // [it, end()), [begin(), it) is the part that contains the max-heap - // of small buckets. - std::vector<Bucket>::iterator it; - for (it = buckets.end(); it != buckets.begin(); --it) { - if (buckets.front().size < min_size_bytes) - break; - - // Put the largest bucket in [begin, it) at |it - 1| and max-heapify - // [begin, it - 1). This puts the next largest bucket at |buckets.front()|. - std::pop_heap(buckets.begin(), it); - } - - // At this point, |buckets| looks like this (numbers are bucket sizes): - // - // <-- max-heap of small buckets ---> - // <-- large buckets by ascending size --> - // [ 19 | 11 | 13 | 7 | 2 | 5 | ... | 83 | 89 | 97 ] - // ^ ^ ^ - // | | | - // begin() it end() - - // Discard the long tail of buckets that contribute less than a percent. - buckets.erase(buckets.begin(), it); - - return buckets; -} - -} // namespace - -bool operator<(Entry lhs, Entry rhs) { - // There is no need to compare |size|. If the backtrace and type name are - // equal then the sizes must be equal as well. - return std::tie(lhs.stack_frame_id, lhs.type_id) < - std::tie(rhs.stack_frame_id, rhs.type_id); -} - -HeapDumpWriter::HeapDumpWriter(StackFrameDeduplicator* stack_frame_deduplicator, - TypeNameDeduplicator* type_name_deduplicator, - uint32_t breakdown_threshold_bytes) - : stack_frame_deduplicator_(stack_frame_deduplicator), - type_name_deduplicator_(type_name_deduplicator), - breakdown_threshold_bytes_(breakdown_threshold_bytes) { -} - -HeapDumpWriter::~HeapDumpWriter() {} - -bool HeapDumpWriter::AddEntryForBucket(const Bucket& bucket) { - // The contexts in the bucket are all different, but the [begin, cursor) range - // is equal for all contexts in the bucket, and the type names are the same if - // |is_broken_down_by_type_name| is set. - DCHECK(!bucket.metrics_by_context.empty()); - - const AllocationContext* context = bucket.metrics_by_context.front().first; - - const StackFrame* backtrace_begin = std::begin(context->backtrace.frames); - const StackFrame* backtrace_end = backtrace_begin + bucket.backtrace_cursor; - DCHECK_LE(bucket.backtrace_cursor, arraysize(context->backtrace.frames)); - - Entry entry; - entry.stack_frame_id = stack_frame_deduplicator_->Insert( - backtrace_begin, backtrace_end); - - // Deduplicate the type name, or use ID -1 if type name is not set. - entry.type_id = bucket.is_broken_down_by_type_name - ? type_name_deduplicator_->Insert(context->type_name) - : -1; - - entry.size = bucket.size; - entry.count = bucket.count; - - auto position_and_inserted = entries_.insert(entry); - return position_and_inserted.second; -} - -void HeapDumpWriter::BreakDown(const Bucket& bucket) { - auto by_backtrace = BreakDownBy(bucket, - BreakDownMode::kByBacktrace, - breakdown_threshold_bytes_); - auto by_type_name = BreakDownBy(bucket, - BreakDownMode::kByTypeName, - breakdown_threshold_bytes_); - - // Insert entries for the buckets. If a bucket was not present before, it has - // not been broken down before, so recursively continue breaking down in that - // case. There might be multiple routes to the same entry (first break down - // by type name, then by backtrace, or first by backtrace and then by type), - // so a set is used to avoid dumping and breaking down entries more than once. - - for (const Bucket& subbucket : by_backtrace) - if (AddEntryForBucket(subbucket)) - BreakDown(subbucket); - - for (const Bucket& subbucket : by_type_name) - if (AddEntryForBucket(subbucket)) - BreakDown(subbucket); -} - -const std::set<Entry>& HeapDumpWriter::Summarize( - const hash_map<AllocationContext, AllocationMetrics>& metrics_by_context) { - // Start with one bucket that represents the entire heap. Iterate by - // reference, because the allocation contexts are going to point to allocation - // contexts stored in |metrics_by_context|. - Bucket root_bucket; - for (const auto& context_and_metrics : metrics_by_context) { - DCHECK_GT(context_and_metrics.second.size, 0u); - DCHECK_GT(context_and_metrics.second.count, 0u); - const AllocationContext* context = &context_and_metrics.first; - root_bucket.metrics_by_context.push_back( - std::make_pair(context, context_and_metrics.second)); - root_bucket.size += context_and_metrics.second.size; - root_bucket.count += context_and_metrics.second.count; - } - - AddEntryForBucket(root_bucket); - - // Recursively break down the heap and fill |entries_| with entries to dump. - BreakDown(root_bucket); - - return entries_; -} - -std::unique_ptr<TracedValue> Serialize(const std::set<Entry>& entries) { - std::string buffer; - std::unique_ptr<TracedValue> traced_value(new TracedValue); - - traced_value->BeginArray("entries"); - - for (const Entry& entry : entries) { - traced_value->BeginDictionary(); - - // Format size as hexadecimal string into |buffer|. - SStringPrintf(&buffer, "%" PRIx64, static_cast<uint64_t>(entry.size)); - traced_value->SetString("size", buffer); - - SStringPrintf(&buffer, "%" PRIx64, static_cast<uint64_t>(entry.count)); - traced_value->SetString("count", buffer); - - if (entry.stack_frame_id == -1) { - // An empty backtrace (which will have ID -1) is represented by the empty - // string, because there is no leaf frame to reference in |stackFrames|. - traced_value->SetString("bt", ""); - } else { - // Format index of the leaf frame as a string, because |stackFrames| is a - // dictionary, not an array. - SStringPrintf(&buffer, "%i", entry.stack_frame_id); - traced_value->SetString("bt", buffer); - } - - // Type ID -1 (cumulative size for all types) is represented by the absence - // of the "type" key in the dictionary. - if (entry.type_id != -1) { - // Format the type ID as a string. - SStringPrintf(&buffer, "%i", entry.type_id); - traced_value->SetString("type", buffer); - } - - traced_value->EndDictionary(); - } - - traced_value->EndArray(); // "entries" - return traced_value; -} - -} // namespace internal - -std::unique_ptr<TracedValue> ExportHeapDump( - const hash_map<AllocationContext, AllocationMetrics>& metrics_by_context, - const MemoryDumpSessionState& session_state) { - internal::HeapDumpWriter writer( - session_state.stack_frame_deduplicator(), - session_state.type_name_deduplicator(), - session_state.heap_profiler_breakdown_threshold_bytes()); - return Serialize(writer.Summarize(metrics_by_context)); -} - -} // namespace trace_event -} // namespace base diff --git a/base/trace_event/heap_profiler_heap_dump_writer.h b/base/trace_event/heap_profiler_heap_dump_writer.h deleted file mode 100644 index 6e9d29de87..0000000000 --- a/base/trace_event/heap_profiler_heap_dump_writer.h +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_TRACE_EVENT_HEAP_PROFILER_HEAP_DUMP_WRITER_H_ -#define BASE_TRACE_EVENT_HEAP_PROFILER_HEAP_DUMP_WRITER_H_ - -#include <stddef.h> - -#include <memory> -#include <set> - -#include "base/base_export.h" -#include "base/containers/hash_tables.h" -#include "base/macros.h" -#include "base/trace_event/heap_profiler_allocation_context.h" - -namespace base { -namespace trace_event { - -class MemoryDumpSessionState; -class StackFrameDeduplicator; -class TracedValue; -class TypeNameDeduplicator; - -// Aggregates |metrics_by_context|, recursively breaks down the heap, and -// returns a traced value with an "entries" array that can be dumped in the -// trace log, following the format described in https://goo.gl/KY7zVE. The -// number of entries is kept reasonable because long tails are not included. -BASE_EXPORT std::unique_ptr<TracedValue> ExportHeapDump( - const hash_map<AllocationContext, AllocationMetrics>& metrics_by_context, - const MemoryDumpSessionState& session_state); - -namespace internal { - -namespace { -struct Bucket; -} - -// An entry in the "entries" array as described in https://goo.gl/KY7zVE. -struct BASE_EXPORT Entry { - size_t size; - size_t count; - - // References a backtrace in the stack frame deduplicator. -1 means empty - // backtrace (the root of the tree). - int stack_frame_id; - - // References a type name in the type name deduplicator. -1 indicates that - // the size is the cumulative size for all types (the root of the tree). - int type_id; -}; - -// Comparison operator to enable putting |Entry| in a |std::set|. -BASE_EXPORT bool operator<(Entry lhs, Entry rhs); - -// Serializes entries to an "entries" array in a traced value. -BASE_EXPORT std::unique_ptr<TracedValue> Serialize(const std::set<Entry>& dump); - -// Helper class to dump a snapshot of an |AllocationRegister| or other heap -// bookkeeping structure into a |TracedValue|. This class is intended to be -// used as a one-shot local instance on the stack. -class BASE_EXPORT HeapDumpWriter { - public: - // The |stack_frame_deduplicator| and |type_name_deduplicator| are not owned. - // The heap dump writer assumes exclusive access to them during the lifetime - // of the dump writer. The heap dumps are broken down for allocations bigger - // than |breakdown_threshold_bytes|. - HeapDumpWriter(StackFrameDeduplicator* stack_frame_deduplicator, - TypeNameDeduplicator* type_name_deduplicator, - uint32_t breakdown_threshold_bytes); - - ~HeapDumpWriter(); - - // Aggregates allocations to compute the total size of the heap, then breaks - // down the heap recursively. This produces the values that should be dumped - // in the "entries" array. The number of entries is kept reasonable because - // long tails are not included. Use |Serialize| to convert to a traced value. - const std::set<Entry>& Summarize( - const hash_map<AllocationContext, AllocationMetrics>& metrics_by_context); - - private: - // Inserts an |Entry| for |Bucket| into |entries_|. Returns false if the - // entry was present before, true if it was not. - bool AddEntryForBucket(const Bucket& bucket); - - // Recursively breaks down a bucket into smaller buckets and adds entries for - // the buckets worth dumping to |entries_|. - void BreakDown(const Bucket& bucket); - - // The collection of entries that is filled by |Summarize|. - std::set<Entry> entries_; - - // Helper for generating the |stackFrames| dictionary. Not owned, must outlive - // this heap dump writer instance. - StackFrameDeduplicator* const stack_frame_deduplicator_; - - // Helper for converting type names to IDs. Not owned, must outlive this heap - // dump writer instance. - TypeNameDeduplicator* const type_name_deduplicator_; - - // Minimum size of an allocation for which an allocation bucket will be - // broken down with children. - uint32_t breakdown_threshold_bytes_; - - DISALLOW_COPY_AND_ASSIGN(HeapDumpWriter); -}; - -} // namespace internal -} // namespace trace_event -} // namespace base - -#endif // BASE_TRACE_EVENT_HEAP_PROFILER_HEAP_DUMP_WRITER_H_ diff --git a/base/trace_event/heap_profiler_stack_frame_deduplicator.cc b/base/trace_event/heap_profiler_stack_frame_deduplicator.cc deleted file mode 100644 index fc5da0d1dd..0000000000 --- a/base/trace_event/heap_profiler_stack_frame_deduplicator.cc +++ /dev/null @@ -1,131 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/trace_event/heap_profiler_stack_frame_deduplicator.h" - -#include <inttypes.h> -#include <stddef.h> - -#include <string> -#include <utility> - -#include "base/strings/stringprintf.h" -#include "base/trace_event/memory_usage_estimator.h" -#include "base/trace_event/trace_event_argument.h" -#include "base/trace_event/trace_event_memory_overhead.h" - -namespace base { -namespace trace_event { - -StackFrameDeduplicator::FrameNode::FrameNode(StackFrame frame, - int parent_frame_index) - : frame(frame), parent_frame_index(parent_frame_index) {} -StackFrameDeduplicator::FrameNode::FrameNode(const FrameNode& other) = default; -StackFrameDeduplicator::FrameNode::~FrameNode() {} - -size_t StackFrameDeduplicator::FrameNode::EstimateMemoryUsage() const { - return base::trace_event::EstimateMemoryUsage(children); -} - -StackFrameDeduplicator::StackFrameDeduplicator() {} -StackFrameDeduplicator::~StackFrameDeduplicator() {} - -int StackFrameDeduplicator::Insert(const StackFrame* beginFrame, - const StackFrame* endFrame) { - int frame_index = -1; - std::map<StackFrame, int>* nodes = &roots_; - - // Loop through the frames, early out when a frame is null. - for (const StackFrame* it = beginFrame; it != endFrame; it++) { - StackFrame frame = *it; - - auto node = nodes->find(frame); - if (node == nodes->end()) { - // There is no tree node for this frame yet, create it. The parent node - // is the node associated with the previous frame. - FrameNode frame_node(frame, frame_index); - - // The new frame node will be appended, so its index is the current size - // of the vector. - frame_index = static_cast<int>(frames_.size()); - - // Add the node to the trie so it will be found next time. - nodes->insert(std::make_pair(frame, frame_index)); - - // Append the node after modifying |nodes|, because the |frames_| vector - // might need to resize, and this invalidates the |nodes| pointer. - frames_.push_back(frame_node); - } else { - // A tree node for this frame exists. Look for the next one. - frame_index = node->second; - } - - nodes = &frames_[frame_index].children; - } - - return frame_index; -} - -void StackFrameDeduplicator::AppendAsTraceFormat(std::string* out) const { - out->append("{"); // Begin the |stackFrames| dictionary. - - int i = 0; - auto frame_node = begin(); - auto it_end = end(); - std::string stringify_buffer; - - while (frame_node != it_end) { - // The |stackFrames| format is a dictionary, not an array, so the - // keys are stringified indices. Write the index manually, then use - // |TracedValue| to format the object. This is to avoid building the - // entire dictionary as a |TracedValue| in memory. - SStringPrintf(&stringify_buffer, "\"%d\":", i); - out->append(stringify_buffer); - - std::unique_ptr<TracedValue> frame_node_value(new TracedValue); - const StackFrame& frame = frame_node->frame; - switch (frame.type) { - case StackFrame::Type::TRACE_EVENT_NAME: - frame_node_value->SetString( - "name", static_cast<const char*>(frame.value)); - break; - case StackFrame::Type::THREAD_NAME: - SStringPrintf(&stringify_buffer, - "[Thread: %s]", - static_cast<const char*>(frame.value)); - frame_node_value->SetString("name", stringify_buffer); - break; - case StackFrame::Type::PROGRAM_COUNTER: - SStringPrintf(&stringify_buffer, - "pc:%" PRIxPTR, - reinterpret_cast<uintptr_t>(frame.value)); - frame_node_value->SetString("name", stringify_buffer); - break; - } - if (frame_node->parent_frame_index >= 0) { - SStringPrintf(&stringify_buffer, "%d", frame_node->parent_frame_index); - frame_node_value->SetString("parent", stringify_buffer); - } - frame_node_value->AppendAsTraceFormat(out); - - i++; - frame_node++; - - if (frame_node != it_end) - out->append(","); - } - - out->append("}"); // End the |stackFrames| dictionary. -} - -void StackFrameDeduplicator::EstimateTraceMemoryOverhead( - TraceEventMemoryOverhead* overhead) { - size_t memory_usage = - EstimateMemoryUsage(frames_) + EstimateMemoryUsage(roots_); - overhead->Add("StackFrameDeduplicator", - sizeof(StackFrameDeduplicator) + memory_usage); -} - -} // namespace trace_event -} // namespace base diff --git a/base/trace_event/heap_profiler_stack_frame_deduplicator.h b/base/trace_event/heap_profiler_stack_frame_deduplicator.h deleted file mode 100644 index 66d430f2ee..0000000000 --- a/base/trace_event/heap_profiler_stack_frame_deduplicator.h +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_TRACE_EVENT_HEAP_PROFILER_STACK_FRAME_DEDUPLICATOR_H_ -#define BASE_TRACE_EVENT_HEAP_PROFILER_STACK_FRAME_DEDUPLICATOR_H_ - -#include <map> -#include <string> -#include <vector> - -#include "base/base_export.h" -#include "base/macros.h" -#include "base/trace_event/heap_profiler_allocation_context.h" -#include "base/trace_event/trace_event_impl.h" - -namespace base { -namespace trace_event { - -class TraceEventMemoryOverhead; - -// A data structure that allows grouping a set of backtraces in a space- -// efficient manner by creating a call tree and writing it as a set of (node, -// parent) pairs. The tree nodes reference both parent and children. The parent -// is referenced by index into |frames_|. The children are referenced via a map -// of |StackFrame|s to index into |frames_|. So there is a trie for bottum-up -// lookup of a backtrace for deduplication, and a tree for compact storage in -// the trace log. -class BASE_EXPORT StackFrameDeduplicator : public ConvertableToTraceFormat { - public: - // A node in the call tree. - struct FrameNode { - FrameNode(StackFrame frame, int parent_frame_index); - FrameNode(const FrameNode& other); - ~FrameNode(); - - size_t EstimateMemoryUsage() const; - - StackFrame frame; - - // The index of the parent stack frame in |frames_|, or -1 if there is no - // parent frame (when it is at the bottom of the call stack). - int parent_frame_index; - - // Indices into |frames_| of frames called from the current frame. - std::map<StackFrame, int> children; - }; - - using ConstIterator = std::vector<FrameNode>::const_iterator; - - StackFrameDeduplicator(); - ~StackFrameDeduplicator() override; - - // Inserts a backtrace where |beginFrame| is a pointer to the bottom frame - // (e.g. main) and |endFrame| is a pointer past the top frame (most recently - // called function), and returns the index of its leaf node in |frames_|. - // Returns -1 if the backtrace is empty. - int Insert(const StackFrame* beginFrame, const StackFrame* endFrame); - - // Iterators over the frame nodes in the call tree. - ConstIterator begin() const { return frames_.begin(); } - ConstIterator end() const { return frames_.end(); } - - // Writes the |stackFrames| dictionary as defined in https://goo.gl/GerkV8 to - // the trace log. - void AppendAsTraceFormat(std::string* out) const override; - - // Estimates memory overhead including |sizeof(StackFrameDeduplicator)|. - void EstimateTraceMemoryOverhead(TraceEventMemoryOverhead* overhead) override; - - private: - std::map<StackFrame, int> roots_; - std::vector<FrameNode> frames_; - - DISALLOW_COPY_AND_ASSIGN(StackFrameDeduplicator); -}; - -} // namespace trace_event -} // namespace base - -#endif // BASE_TRACE_EVENT_HEAP_PROFILER_STACK_FRAME_DEDUPLICATOR_H_ diff --git a/base/trace_event/heap_profiler_stack_frame_deduplicator_unittest.cc b/base/trace_event/heap_profiler_stack_frame_deduplicator_unittest.cc deleted file mode 100644 index 2215edebb5..0000000000 --- a/base/trace_event/heap_profiler_stack_frame_deduplicator_unittest.cc +++ /dev/null @@ -1,152 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/trace_event/heap_profiler_stack_frame_deduplicator.h" - -#include <iterator> -#include <memory> - -#include "base/macros.h" -#include "base/trace_event/heap_profiler_allocation_context.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { -namespace trace_event { - -// Define all strings once, because the deduplicator requires pointer equality, -// and string interning is unreliable. -StackFrame kBrowserMain = StackFrame::FromTraceEventName("BrowserMain"); -StackFrame kRendererMain = StackFrame::FromTraceEventName("RendererMain"); -StackFrame kCreateWidget = StackFrame::FromTraceEventName("CreateWidget"); -StackFrame kInitialize = StackFrame::FromTraceEventName("Initialize"); -StackFrame kMalloc = StackFrame::FromTraceEventName("malloc"); - -TEST(StackFrameDeduplicatorTest, SingleBacktrace) { - StackFrame bt[] = {kBrowserMain, kCreateWidget, kMalloc}; - - // The call tree should look like this (index in brackets). - // - // BrowserMain [0] - // CreateWidget [1] - // malloc [2] - - std::unique_ptr<StackFrameDeduplicator> dedup(new StackFrameDeduplicator); - ASSERT_EQ(2, dedup->Insert(std::begin(bt), std::end(bt))); - - auto iter = dedup->begin(); - ASSERT_EQ(kBrowserMain, (iter + 0)->frame); - ASSERT_EQ(-1, (iter + 0)->parent_frame_index); - - ASSERT_EQ(kCreateWidget, (iter + 1)->frame); - ASSERT_EQ(0, (iter + 1)->parent_frame_index); - - ASSERT_EQ(kMalloc, (iter + 2)->frame); - ASSERT_EQ(1, (iter + 2)->parent_frame_index); - - ASSERT_EQ(iter + 3, dedup->end()); -} - -TEST(StackFrameDeduplicatorTest, SingleBacktraceWithNull) { - StackFrame null_frame = StackFrame::FromTraceEventName(nullptr); - StackFrame bt[] = {kBrowserMain, null_frame, kMalloc}; - - // Deduplicator doesn't care about what's inside StackFrames, - // and handles nullptr StackFrame values as any other. - // - // So the call tree should look like this (index in brackets). - // - // BrowserMain [0] - // (null) [1] - // malloc [2] - - std::unique_ptr<StackFrameDeduplicator> dedup(new StackFrameDeduplicator); - ASSERT_EQ(2, dedup->Insert(std::begin(bt), std::end(bt))); - - auto iter = dedup->begin(); - ASSERT_EQ(kBrowserMain, (iter + 0)->frame); - ASSERT_EQ(-1, (iter + 0)->parent_frame_index); - - ASSERT_EQ(null_frame, (iter + 1)->frame); - ASSERT_EQ(0, (iter + 1)->parent_frame_index); - - ASSERT_EQ(kMalloc, (iter + 2)->frame); - ASSERT_EQ(1, (iter + 2)->parent_frame_index); - - ASSERT_EQ(iter + 3, dedup->end()); -} - -// Test that there can be different call trees (there can be multiple bottom -// frames). Also verify that frames with the same name but a different caller -// are represented as distinct nodes. -TEST(StackFrameDeduplicatorTest, MultipleRoots) { - StackFrame bt0[] = {kBrowserMain, kCreateWidget}; - StackFrame bt1[] = {kRendererMain, kCreateWidget}; - - // The call tree should look like this (index in brackets). - // - // BrowserMain [0] - // CreateWidget [1] - // RendererMain [2] - // CreateWidget [3] - // - // Note that there will be two instances of CreateWidget, - // with different parents. - - std::unique_ptr<StackFrameDeduplicator> dedup(new StackFrameDeduplicator); - ASSERT_EQ(1, dedup->Insert(std::begin(bt0), std::end(bt0))); - ASSERT_EQ(3, dedup->Insert(std::begin(bt1), std::end(bt1))); - - auto iter = dedup->begin(); - ASSERT_EQ(kBrowserMain, (iter + 0)->frame); - ASSERT_EQ(-1, (iter + 0)->parent_frame_index); - - ASSERT_EQ(kCreateWidget, (iter + 1)->frame); - ASSERT_EQ(0, (iter + 1)->parent_frame_index); - - ASSERT_EQ(kRendererMain, (iter + 2)->frame); - ASSERT_EQ(-1, (iter + 2)->parent_frame_index); - - ASSERT_EQ(kCreateWidget, (iter + 3)->frame); - ASSERT_EQ(2, (iter + 3)->parent_frame_index); - - ASSERT_EQ(iter + 4, dedup->end()); -} - -TEST(StackFrameDeduplicatorTest, Deduplication) { - StackFrame bt0[] = {kBrowserMain, kCreateWidget}; - StackFrame bt1[] = {kBrowserMain, kInitialize}; - - // The call tree should look like this (index in brackets). - // - // BrowserMain [0] - // CreateWidget [1] - // Initialize [2] - // - // Note that BrowserMain will be re-used. - - std::unique_ptr<StackFrameDeduplicator> dedup(new StackFrameDeduplicator); - ASSERT_EQ(1, dedup->Insert(std::begin(bt0), std::end(bt0))); - ASSERT_EQ(2, dedup->Insert(std::begin(bt1), std::end(bt1))); - - auto iter = dedup->begin(); - ASSERT_EQ(kBrowserMain, (iter + 0)->frame); - ASSERT_EQ(-1, (iter + 0)->parent_frame_index); - - ASSERT_EQ(kCreateWidget, (iter + 1)->frame); - ASSERT_EQ(0, (iter + 1)->parent_frame_index); - - ASSERT_EQ(kInitialize, (iter + 2)->frame); - ASSERT_EQ(0, (iter + 2)->parent_frame_index); - - ASSERT_EQ(iter + 3, dedup->end()); - - // Inserting the same backtrace again should return the index of the existing - // node. - ASSERT_EQ(1, dedup->Insert(std::begin(bt0), std::end(bt0))); - ASSERT_EQ(2, dedup->Insert(std::begin(bt1), std::end(bt1))); - ASSERT_EQ(dedup->begin() + 3, dedup->end()); -} - -} // namespace trace_event -} // namespace base diff --git a/base/trace_event/heap_profiler_type_name_deduplicator.cc b/base/trace_event/heap_profiler_type_name_deduplicator.cc deleted file mode 100644 index a6dab51ad2..0000000000 --- a/base/trace_event/heap_profiler_type_name_deduplicator.cc +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/trace_event/heap_profiler_type_name_deduplicator.h" - -#include <stddef.h> -#include <stdlib.h> -#include <string> -#include <utility> - -#include "base/json/string_escape.h" -#include "base/strings/string_split.h" -#include "base/strings/stringprintf.h" -#include "base/trace_event/memory_usage_estimator.h" -#include "base/trace_event/trace_event.h" -#include "base/trace_event/trace_event_memory_overhead.h" - -namespace base { -namespace trace_event { - -namespace { - -// If |type_name| is file name then extract directory name. Or if |type_name| is -// category name, then disambiguate multple categories and remove -// "disabled-by-default" prefix if present. -StringPiece ExtractCategoryFromTypeName(const char* type_name) { - StringPiece result(type_name); - size_t last_seperator = result.find_last_of("\\/"); - - // If |type_name| was a not a file path, the seperator will not be found, so - // the whole type name is returned. - if (last_seperator == StringPiece::npos) { - // Use the first the category name if it has ",". - size_t first_comma_position = result.find(','); - if (first_comma_position != StringPiece::npos) - result = result.substr(0, first_comma_position); - if (result.starts_with(TRACE_DISABLED_BY_DEFAULT(""))) - result.remove_prefix(sizeof(TRACE_DISABLED_BY_DEFAULT("")) - 1); - return result; - } - - // Remove the file name from the path. - result.remove_suffix(result.length() - last_seperator); - - // Remove the parent directory references. - const char kParentDirectory[] = ".."; - const size_t kParentDirectoryLength = 3; // '../' or '..\'. - while (result.starts_with(kParentDirectory)) { - result.remove_prefix(kParentDirectoryLength); - } - return result; -} - -} // namespace - -TypeNameDeduplicator::TypeNameDeduplicator() { - // A null pointer has type ID 0 ("unknown type"); - type_ids_.insert(std::make_pair(nullptr, 0)); -} - -TypeNameDeduplicator::~TypeNameDeduplicator() {} - -int TypeNameDeduplicator::Insert(const char* type_name) { - auto result = type_ids_.insert(std::make_pair(type_name, 0)); - auto& elem = result.first; - bool did_not_exist_before = result.second; - - if (did_not_exist_before) { - // The type IDs are assigned sequentially and they are zero-based, so - // |size() - 1| is the ID of the new element. - elem->second = static_cast<int>(type_ids_.size() - 1); - } - - return elem->second; -} - -void TypeNameDeduplicator::AppendAsTraceFormat(std::string* out) const { - out->append("{"); // Begin the type names dictionary. - - auto it = type_ids_.begin(); - std::string buffer; - - // Write the first entry manually; the null pointer must not be dereferenced. - // (The first entry is the null pointer because a |std::map| is ordered.) - it++; - out->append("\"0\":\"[unknown]\""); - - for (; it != type_ids_.end(); it++) { - // Type IDs in the trace are strings, write them as stringified keys of - // a dictionary. - SStringPrintf(&buffer, ",\"%d\":", it->second); - - // TODO(ssid): crbug.com/594803 the type name is misused for file name in - // some cases. - StringPiece type_info = ExtractCategoryFromTypeName(it->first); - - // |EscapeJSONString| appends, it does not overwrite |buffer|. - bool put_in_quotes = true; - EscapeJSONString(type_info, put_in_quotes, &buffer); - out->append(buffer); - } - - out->append("}"); // End the type names dictionary. -} - -void TypeNameDeduplicator::EstimateTraceMemoryOverhead( - TraceEventMemoryOverhead* overhead) { - size_t memory_usage = EstimateMemoryUsage(type_ids_); - overhead->Add("TypeNameDeduplicator", - sizeof(TypeNameDeduplicator) + memory_usage); -} - -} // namespace trace_event -} // namespace base diff --git a/base/trace_event/heap_profiler_type_name_deduplicator.h b/base/trace_event/heap_profiler_type_name_deduplicator.h deleted file mode 100644 index 2d26c73488..0000000000 --- a/base/trace_event/heap_profiler_type_name_deduplicator.h +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_TRACE_EVENT_HEAP_PROFILER_TYPE_NAME_DEDUPLICATOR_H_ -#define BASE_TRACE_EVENT_HEAP_PROFILER_TYPE_NAME_DEDUPLICATOR_H_ - -#include <map> -#include <string> - -#include "base/base_export.h" -#include "base/macros.h" -#include "base/trace_event/trace_event_impl.h" - -namespace base { -namespace trace_event { - -class TraceEventMemoryOverhead; - -// Data structure that assigns a unique numeric ID to |const char*|s. -class BASE_EXPORT TypeNameDeduplicator : public ConvertableToTraceFormat { - public: - TypeNameDeduplicator(); - ~TypeNameDeduplicator() override; - - // Inserts a type name and returns its ID. - int Insert(const char* type_name); - - // Writes the type ID -> type name mapping to the trace log. - void AppendAsTraceFormat(std::string* out) const override; - - // Estimates memory overhead including |sizeof(TypeNameDeduplicator)|. - void EstimateTraceMemoryOverhead(TraceEventMemoryOverhead* overhead) override; - - private: - // Map from type name to type ID. - std::map<const char*, int> type_ids_; - - DISALLOW_COPY_AND_ASSIGN(TypeNameDeduplicator); -}; - -} // namespace trace_event -} // namespace base - -#endif // BASE_TRACE_EVENT_HEAP_PROFILER_TYPE_NAME_DEDUPLICATOR_H_ diff --git a/base/trace_event/heap_profiler_type_name_deduplicator_unittest.cc b/base/trace_event/heap_profiler_type_name_deduplicator_unittest.cc deleted file mode 100644 index b2e681ab26..0000000000 --- a/base/trace_event/heap_profiler_type_name_deduplicator_unittest.cc +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include <memory> -#include <string> - -#include "base/json/json_reader.h" -#include "base/trace_event/heap_profiler_type_name_deduplicator.h" -#include "base/values.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { -namespace trace_event { - -namespace { - -// Define all strings once, because the deduplicator requires pointer equality, -// and string interning is unreliable. -const char kInt[] = "int"; -const char kBool[] = "bool"; -const char kString[] = "string"; -const char kNeedsEscape[] = "\"quotes\""; - -#if defined(OS_POSIX) -const char kTaskFileName[] = "../../base/trace_event/trace_log.cc"; -const char kTaskPath[] = "base/trace_event"; -#else -const char kTaskFileName[] = "..\\..\\base\\memory\\memory_win.cc"; -const char kTaskPath[] = "base\\memory"; -#endif - -std::unique_ptr<Value> DumpAndReadBack( - const TypeNameDeduplicator& deduplicator) { - std::string json; - deduplicator.AppendAsTraceFormat(&json); - return JSONReader::Read(json); -} - -// Inserts a single type name into a new TypeNameDeduplicator instance and -// checks if the value gets inserted and the exported value for |type_name| is -// the same as |expected_value|. -void TestInsertTypeAndReadback(const char* type_name, - const char* expected_value) { - std::unique_ptr<TypeNameDeduplicator> dedup(new TypeNameDeduplicator); - ASSERT_EQ(1, dedup->Insert(type_name)); - - std::unique_ptr<Value> type_names = DumpAndReadBack(*dedup); - ASSERT_NE(nullptr, type_names); - - const DictionaryValue* dictionary; - ASSERT_TRUE(type_names->GetAsDictionary(&dictionary)); - - // When the type name was inserted, it got ID 1. The exported key "1" - // should be equal to |expected_value|. - std::string value; - ASSERT_TRUE(dictionary->GetString("1", &value)); - ASSERT_EQ(expected_value, value); -} - -} // namespace - -TEST(TypeNameDeduplicatorTest, Deduplication) { - // The type IDs should be like this: - // 0: [unknown] - // 1: int - // 2: bool - // 3: string - - std::unique_ptr<TypeNameDeduplicator> dedup(new TypeNameDeduplicator); - ASSERT_EQ(1, dedup->Insert(kInt)); - ASSERT_EQ(2, dedup->Insert(kBool)); - ASSERT_EQ(3, dedup->Insert(kString)); - - // Inserting again should return the same IDs. - ASSERT_EQ(2, dedup->Insert(kBool)); - ASSERT_EQ(1, dedup->Insert(kInt)); - ASSERT_EQ(3, dedup->Insert(kString)); - - // A null pointer should yield type ID 0. - ASSERT_EQ(0, dedup->Insert(nullptr)); -} - -TEST(TypeNameDeduplicatorTest, EscapeTypeName) { - // Reading json should not fail, because the type name should have been - // escaped properly and exported value should contain quotes. - TestInsertTypeAndReadback(kNeedsEscape, kNeedsEscape); -} - -TEST(TypeNameDeduplicatorTest, TestExtractFileName) { - // The exported value for passed file name should be the folders in the path. - TestInsertTypeAndReadback(kTaskFileName, kTaskPath); -} - -} // namespace trace_event -} // namespace base diff --git a/base/trace_event/malloc_dump_provider.cc b/base/trace_event/malloc_dump_provider.cc deleted file mode 100644 index 7f2706092e..0000000000 --- a/base/trace_event/malloc_dump_provider.cc +++ /dev/null @@ -1,378 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/trace_event/malloc_dump_provider.h" - -#include <stddef.h> - -#include "base/allocator/allocator_extension.h" -#include "base/allocator/allocator_shim.h" -#include "base/allocator/features.h" -#include "base/debug/profiler.h" -#include "base/trace_event/heap_profiler_allocation_context.h" -#include "base/trace_event/heap_profiler_allocation_context_tracker.h" -#include "base/trace_event/heap_profiler_allocation_register.h" -#include "base/trace_event/heap_profiler_heap_dump_writer.h" -#include "base/trace_event/process_memory_dump.h" -#include "base/trace_event/trace_event_argument.h" -#include "build/build_config.h" - -#if defined(OS_MACOSX) -#include <malloc/malloc.h> -#else -#include <malloc.h> -#endif -#if defined(OS_WIN) -#include <windows.h> -#endif - -namespace base { -namespace trace_event { - -namespace { -#if BUILDFLAG(USE_EXPERIMENTAL_ALLOCATOR_SHIM) - -using allocator::AllocatorDispatch; - -void* HookAlloc(const AllocatorDispatch* self, size_t size, void* context) { - const AllocatorDispatch* const next = self->next; - void* ptr = next->alloc_function(next, size, context); - if (ptr) - MallocDumpProvider::GetInstance()->InsertAllocation(ptr, size); - return ptr; -} - -void* HookZeroInitAlloc(const AllocatorDispatch* self, - size_t n, - size_t size, - void* context) { - const AllocatorDispatch* const next = self->next; - void* ptr = next->alloc_zero_initialized_function(next, n, size, context); - if (ptr) - MallocDumpProvider::GetInstance()->InsertAllocation(ptr, n * size); - return ptr; -} - -void* HookAllocAligned(const AllocatorDispatch* self, - size_t alignment, - size_t size, - void* context) { - const AllocatorDispatch* const next = self->next; - void* ptr = next->alloc_aligned_function(next, alignment, size, context); - if (ptr) - MallocDumpProvider::GetInstance()->InsertAllocation(ptr, size); - return ptr; -} - -void* HookRealloc(const AllocatorDispatch* self, - void* address, - size_t size, - void* context) { - const AllocatorDispatch* const next = self->next; - void* ptr = next->realloc_function(next, address, size, context); - MallocDumpProvider::GetInstance()->RemoveAllocation(address); - if (size > 0) // realloc(size == 0) means free(). - MallocDumpProvider::GetInstance()->InsertAllocation(ptr, size); - return ptr; -} - -void HookFree(const AllocatorDispatch* self, void* address, void* context) { - if (address) - MallocDumpProvider::GetInstance()->RemoveAllocation(address); - const AllocatorDispatch* const next = self->next; - next->free_function(next, address, context); -} - -size_t HookGetSizeEstimate(const AllocatorDispatch* self, - void* address, - void* context) { - const AllocatorDispatch* const next = self->next; - return next->get_size_estimate_function(next, address, context); -} - -unsigned HookBatchMalloc(const AllocatorDispatch* self, - size_t size, - void** results, - unsigned num_requested, - void* context) { - const AllocatorDispatch* const next = self->next; - unsigned count = - next->batch_malloc_function(next, size, results, num_requested, context); - for (unsigned i = 0; i < count; ++i) { - MallocDumpProvider::GetInstance()->InsertAllocation(results[i], size); - } - return count; -} - -void HookBatchFree(const AllocatorDispatch* self, - void** to_be_freed, - unsigned num_to_be_freed, - void* context) { - const AllocatorDispatch* const next = self->next; - for (unsigned i = 0; i < num_to_be_freed; ++i) { - MallocDumpProvider::GetInstance()->RemoveAllocation(to_be_freed[i]); - } - next->batch_free_function(next, to_be_freed, num_to_be_freed, context); -} - -void HookFreeDefiniteSize(const AllocatorDispatch* self, - void* ptr, - size_t size, - void* context) { - if (ptr) - MallocDumpProvider::GetInstance()->RemoveAllocation(ptr); - const AllocatorDispatch* const next = self->next; - next->free_definite_size_function(next, ptr, size, context); -} - -AllocatorDispatch g_allocator_hooks = { - &HookAlloc, /* alloc_function */ - &HookZeroInitAlloc, /* alloc_zero_initialized_function */ - &HookAllocAligned, /* alloc_aligned_function */ - &HookRealloc, /* realloc_function */ - &HookFree, /* free_function */ - &HookGetSizeEstimate, /* get_size_estimate_function */ - &HookBatchMalloc, /* batch_malloc_function */ - &HookBatchFree, /* batch_free_function */ - &HookFreeDefiniteSize, /* free_definite_size_function */ - nullptr, /* next */ -}; -#endif // BUILDFLAG(USE_EXPERIMENTAL_ALLOCATOR_SHIM) - -#if defined(OS_WIN) -// A structure containing some information about a given heap. -struct WinHeapInfo { - size_t committed_size; - size_t uncommitted_size; - size_t allocated_size; - size_t block_count; -}; - -// NOTE: crbug.com/665516 -// Unfortunately, there is no safe way to collect information from secondary -// heaps due to limitations and racy nature of this piece of WinAPI. -void WinHeapMemoryDumpImpl(WinHeapInfo* crt_heap_info) { -#if defined(SYZYASAN) - if (base::debug::IsBinaryInstrumented()) - return; -#endif - - // Iterate through whichever heap our CRT is using. - HANDLE crt_heap = reinterpret_cast<HANDLE>(_get_heap_handle()); - ::HeapLock(crt_heap); - PROCESS_HEAP_ENTRY heap_entry; - heap_entry.lpData = nullptr; - // Walk over all the entries in the main heap. - while (::HeapWalk(crt_heap, &heap_entry) != FALSE) { - if ((heap_entry.wFlags & PROCESS_HEAP_ENTRY_BUSY) != 0) { - crt_heap_info->allocated_size += heap_entry.cbData; - crt_heap_info->block_count++; - } else if ((heap_entry.wFlags & PROCESS_HEAP_REGION) != 0) { - crt_heap_info->committed_size += heap_entry.Region.dwCommittedSize; - crt_heap_info->uncommitted_size += heap_entry.Region.dwUnCommittedSize; - } - } - CHECK(::HeapUnlock(crt_heap) == TRUE); -} -#endif // defined(OS_WIN) -} // namespace - -// static -const char MallocDumpProvider::kAllocatedObjects[] = "malloc/allocated_objects"; - -// static -MallocDumpProvider* MallocDumpProvider::GetInstance() { - return Singleton<MallocDumpProvider, - LeakySingletonTraits<MallocDumpProvider>>::get(); -} - -MallocDumpProvider::MallocDumpProvider() - : heap_profiler_enabled_(false), tid_dumping_heap_(kInvalidThreadId) {} - -MallocDumpProvider::~MallocDumpProvider() {} - -// Called at trace dump point time. Creates a snapshot the memory counters for -// the current process. -bool MallocDumpProvider::OnMemoryDump(const MemoryDumpArgs& args, - ProcessMemoryDump* pmd) { - size_t total_virtual_size = 0; - size_t resident_size = 0; - size_t allocated_objects_size = 0; - size_t allocated_objects_count = 0; -#if defined(USE_TCMALLOC) - bool res = - allocator::GetNumericProperty("generic.heap_size", &total_virtual_size); - DCHECK(res); - res = allocator::GetNumericProperty("generic.total_physical_bytes", - &resident_size); - DCHECK(res); - res = allocator::GetNumericProperty("generic.current_allocated_bytes", - &allocated_objects_size); - DCHECK(res); -#elif defined(OS_MACOSX) || defined(OS_IOS) - malloc_statistics_t stats = {0}; - malloc_zone_statistics(nullptr, &stats); - total_virtual_size = stats.size_allocated; - allocated_objects_size = stats.size_in_use; - - // Resident size is approximated pretty well by stats.max_size_in_use. - // However, on macOS, freed blocks are both resident and reusable, which is - // semantically equivalent to deallocated. The implementation of libmalloc - // will also only hold a fixed number of freed regions before actually - // starting to deallocate them, so stats.max_size_in_use is also not - // representative of the peak size. As a result, stats.max_size_in_use is - // typically somewhere between actually resident [non-reusable] pages, and - // peak size. This is not very useful, so we just use stats.size_in_use for - // resident_size, even though it's an underestimate and fails to account for - // fragmentation. See - // https://bugs.chromium.org/p/chromium/issues/detail?id=695263#c1. - resident_size = stats.size_in_use; -#elif defined(OS_WIN) - WinHeapInfo main_heap_info = {}; - WinHeapMemoryDumpImpl(&main_heap_info); - total_virtual_size = - main_heap_info.committed_size + main_heap_info.uncommitted_size; - // Resident size is approximated with committed heap size. Note that it is - // possible to do this with better accuracy on windows by intersecting the - // working set with the virtual memory ranges occuipied by the heap. It's not - // clear that this is worth it, as it's fairly expensive to do. - resident_size = main_heap_info.committed_size; - allocated_objects_size = main_heap_info.allocated_size; - allocated_objects_count = main_heap_info.block_count; -#else - struct mallinfo info = mallinfo(); - DCHECK_GE(info.arena + info.hblkhd, info.uordblks); - - // In case of Android's jemalloc |arena| is 0 and the outer pages size is - // reported by |hblkhd|. In case of dlmalloc the total is given by - // |arena| + |hblkhd|. For more details see link: http://goo.gl/fMR8lF. - total_virtual_size = info.arena + info.hblkhd; - resident_size = info.uordblks; - - // Total allocated space is given by |uordblks|. - allocated_objects_size = info.uordblks; -#endif - - MemoryAllocatorDump* outer_dump = pmd->CreateAllocatorDump("malloc"); - outer_dump->AddScalar("virtual_size", MemoryAllocatorDump::kUnitsBytes, - total_virtual_size); - outer_dump->AddScalar(MemoryAllocatorDump::kNameSize, - MemoryAllocatorDump::kUnitsBytes, resident_size); - - MemoryAllocatorDump* inner_dump = pmd->CreateAllocatorDump(kAllocatedObjects); - inner_dump->AddScalar(MemoryAllocatorDump::kNameSize, - MemoryAllocatorDump::kUnitsBytes, - allocated_objects_size); - if (allocated_objects_count != 0) { - inner_dump->AddScalar(MemoryAllocatorDump::kNameObjectCount, - MemoryAllocatorDump::kUnitsObjects, - allocated_objects_count); - } - - if (resident_size > allocated_objects_size) { - // Explicitly specify why is extra memory resident. In tcmalloc it accounts - // for free lists and caches. In mac and ios it accounts for the - // fragmentation and metadata. - MemoryAllocatorDump* other_dump = - pmd->CreateAllocatorDump("malloc/metadata_fragmentation_caches"); - other_dump->AddScalar(MemoryAllocatorDump::kNameSize, - MemoryAllocatorDump::kUnitsBytes, - resident_size - allocated_objects_size); - } - - // Heap profiler dumps. - if (!heap_profiler_enabled_) - return true; - - // The dumps of the heap profiler should be created only when heap profiling - // was enabled (--enable-heap-profiling) AND a DETAILED dump is requested. - // However, when enabled, the overhead of the heap profiler should be always - // reported to avoid oscillations of the malloc total in LIGHT dumps. - - tid_dumping_heap_ = PlatformThread::CurrentId(); - // At this point the Insert/RemoveAllocation hooks will ignore this thread. - // Enclosing all the temporariy data structures in a scope, so that the heap - // profiler does not see unabalanced malloc/free calls from these containers. - { - TraceEventMemoryOverhead overhead; - hash_map<AllocationContext, AllocationMetrics> metrics_by_context; - { - AutoLock lock(allocation_register_lock_); - if (allocation_register_) { - if (args.level_of_detail == MemoryDumpLevelOfDetail::DETAILED) { - for (const auto& alloc_size : *allocation_register_) { - AllocationMetrics& metrics = metrics_by_context[alloc_size.context]; - metrics.size += alloc_size.size; - metrics.count++; - } - } - allocation_register_->EstimateTraceMemoryOverhead(&overhead); - } - } // lock(allocation_register_lock_) - pmd->DumpHeapUsage(metrics_by_context, overhead, "malloc"); - } - tid_dumping_heap_ = kInvalidThreadId; - - return true; -} - -void MallocDumpProvider::OnHeapProfilingEnabled(bool enabled) { -#if BUILDFLAG(USE_EXPERIMENTAL_ALLOCATOR_SHIM) - if (enabled) { - { - AutoLock lock(allocation_register_lock_); - allocation_register_.reset(new AllocationRegister()); - } - allocator::InsertAllocatorDispatch(&g_allocator_hooks); - } else { - AutoLock lock(allocation_register_lock_); - allocation_register_.reset(); - // Insert/RemoveAllocation below will no-op if the register is torn down. - // Once disabled, heap profiling will not re-enabled anymore for the - // lifetime of the process. - } -#endif - heap_profiler_enabled_ = enabled; -} - -void MallocDumpProvider::InsertAllocation(void* address, size_t size) { - // CurrentId() can be a slow operation (crbug.com/497226). This apparently - // redundant condition short circuits the CurrentID() calls when unnecessary. - if (tid_dumping_heap_ != kInvalidThreadId && - tid_dumping_heap_ == PlatformThread::CurrentId()) - return; - - // AllocationContextTracker will return nullptr when called re-reentrantly. - // This is the case of GetInstanceForCurrentThread() being called for the - // first time, which causes a new() inside the tracker which re-enters the - // heap profiler, in which case we just want to early out. - auto* tracker = AllocationContextTracker::GetInstanceForCurrentThread(); - if (!tracker) - return; - - AllocationContext context; - if (!tracker->GetContextSnapshot(&context)) - return; - - AutoLock lock(allocation_register_lock_); - if (!allocation_register_) - return; - - allocation_register_->Insert(address, size, context); -} - -void MallocDumpProvider::RemoveAllocation(void* address) { - // No re-entrancy is expected here as none of the calls below should - // cause a free()-s (|allocation_register_| does its own heap management). - if (tid_dumping_heap_ != kInvalidThreadId && - tid_dumping_heap_ == PlatformThread::CurrentId()) - return; - AutoLock lock(allocation_register_lock_); - if (!allocation_register_) - return; - allocation_register_->Remove(address); -} - -} // namespace trace_event -} // namespace base diff --git a/base/trace_event/malloc_dump_provider.h b/base/trace_event/malloc_dump_provider.h deleted file mode 100644 index 384033c9b8..0000000000 --- a/base/trace_event/malloc_dump_provider.h +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_TRACE_EVENT_MALLOC_DUMP_PROVIDER_H_ -#define BASE_TRACE_EVENT_MALLOC_DUMP_PROVIDER_H_ - -#include <istream> -#include <memory> - -#include "base/macros.h" -#include "base/memory/singleton.h" -#include "base/synchronization/lock.h" -#include "base/threading/platform_thread.h" -#include "base/trace_event/memory_dump_provider.h" -#include "build/build_config.h" - -#if defined(OS_LINUX) || defined(OS_ANDROID) || defined(OS_WIN) || \ - (defined(OS_MACOSX) && !defined(OS_IOS)) -#define MALLOC_MEMORY_TRACING_SUPPORTED -#endif - -namespace base { -namespace trace_event { - -class AllocationRegister; - -// Dump provider which collects process-wide memory stats. -class BASE_EXPORT MallocDumpProvider : public MemoryDumpProvider { - public: - // Name of the allocated_objects dump. Use this to declare suballocator dumps - // from other dump providers. - static const char kAllocatedObjects[]; - - static MallocDumpProvider* GetInstance(); - - // MemoryDumpProvider implementation. - bool OnMemoryDump(const MemoryDumpArgs& args, - ProcessMemoryDump* pmd) override; - - void OnHeapProfilingEnabled(bool enabled) override; - - // For heap profiling. - void InsertAllocation(void* address, size_t size); - void RemoveAllocation(void* address); - - private: - friend struct DefaultSingletonTraits<MallocDumpProvider>; - - MallocDumpProvider(); - ~MallocDumpProvider() override; - - // For heap profiling. - bool heap_profiler_enabled_; - std::unique_ptr<AllocationRegister> allocation_register_; - Lock allocation_register_lock_; - - // When in OnMemoryDump(), this contains the current thread ID. - // This is to prevent re-entrancy in the heap profiler when the heap dump - // generation is malloc/new-ing for its own bookeeping data structures. - PlatformThreadId tid_dumping_heap_; - - DISALLOW_COPY_AND_ASSIGN(MallocDumpProvider); -}; - -} // namespace trace_event -} // namespace base - -#endif // BASE_TRACE_EVENT_MALLOC_DUMP_PROVIDER_H_ diff --git a/base/trace_event/memory_allocator_dump.cc b/base/trace_event/memory_allocator_dump.cc deleted file mode 100644 index 2692521c09..0000000000 --- a/base/trace_event/memory_allocator_dump.cc +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/trace_event/memory_allocator_dump.h" - -#include "base/format_macros.h" -#include "base/strings/stringprintf.h" -#include "base/trace_event/memory_dump_manager.h" -#include "base/trace_event/memory_dump_provider.h" -#include "base/trace_event/process_memory_dump.h" -#include "base/trace_event/trace_event_argument.h" -#include "base/values.h" - -namespace base { -namespace trace_event { - -const char MemoryAllocatorDump::kNameSize[] = "size"; -const char MemoryAllocatorDump::kNameObjectCount[] = "object_count"; -const char MemoryAllocatorDump::kTypeScalar[] = "scalar"; -const char MemoryAllocatorDump::kTypeString[] = "string"; -const char MemoryAllocatorDump::kUnitsBytes[] = "bytes"; -const char MemoryAllocatorDump::kUnitsObjects[] = "objects"; - -MemoryAllocatorDump::MemoryAllocatorDump(const std::string& absolute_name, - ProcessMemoryDump* process_memory_dump, - const MemoryAllocatorDumpGuid& guid) - : absolute_name_(absolute_name), - process_memory_dump_(process_memory_dump), - attributes_(new TracedValue), - guid_(guid), - flags_(Flags::DEFAULT), - size_(0) { - // The |absolute_name| cannot be empty. - DCHECK(!absolute_name.empty()); - - // The |absolute_name| can contain slash separator, but not leading or - // trailing ones. - DCHECK(absolute_name[0] != '/' && *absolute_name.rbegin() != '/'); -} - -// If the caller didn't provide a guid, make one up by hashing the -// absolute_name with the current PID. -// Rationale: |absolute_name| is already supposed to be unique within a -// process, the pid will make it unique among all processes. -MemoryAllocatorDump::MemoryAllocatorDump(const std::string& absolute_name, - ProcessMemoryDump* process_memory_dump) - : MemoryAllocatorDump(absolute_name, - process_memory_dump, - MemoryAllocatorDumpGuid(StringPrintf( - "%d:%s", - TraceLog::GetInstance()->process_id(), - absolute_name.c_str()))) { - string_conversion_buffer_.reserve(16); -} - -MemoryAllocatorDump::~MemoryAllocatorDump() { -} - -void MemoryAllocatorDump::AddScalar(const char* name, - const char* units, - uint64_t value) { - if (strcmp(kNameSize, name) == 0) - size_ = value; - SStringPrintf(&string_conversion_buffer_, "%" PRIx64, value); - attributes_->BeginDictionary(name); - attributes_->SetString("type", kTypeScalar); - attributes_->SetString("units", units); - attributes_->SetString("value", string_conversion_buffer_); - attributes_->EndDictionary(); -} - -void MemoryAllocatorDump::AddScalarF(const char* name, - const char* units, - double value) { - attributes_->BeginDictionary(name); - attributes_->SetString("type", kTypeScalar); - attributes_->SetString("units", units); - attributes_->SetDouble("value", value); - attributes_->EndDictionary(); -} - -void MemoryAllocatorDump::AddString(const char* name, - const char* units, - const std::string& value) { - // String attributes are disabled in background mode. - if (process_memory_dump_->dump_args().level_of_detail == - MemoryDumpLevelOfDetail::BACKGROUND) { - NOTREACHED(); - return; - } - - attributes_->BeginDictionary(name); - attributes_->SetString("type", kTypeString); - attributes_->SetString("units", units); - attributes_->SetString("value", value); - attributes_->EndDictionary(); -} - -void MemoryAllocatorDump::AsValueInto(TracedValue* value) const { - value->BeginDictionaryWithCopiedName(absolute_name_); - value->SetString("guid", guid_.ToString()); - value->SetValue("attrs", *attributes_); - if (flags_) - value->SetInteger("flags", flags_); - value->EndDictionary(); // "allocator_name/heap_subheap": { ... } -} - -} // namespace trace_event -} // namespace base diff --git a/base/trace_event/memory_allocator_dump.h b/base/trace_event/memory_allocator_dump.h deleted file mode 100644 index 99ff114e5c..0000000000 --- a/base/trace_event/memory_allocator_dump.h +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_TRACE_EVENT_MEMORY_ALLOCATOR_DUMP_H_ -#define BASE_TRACE_EVENT_MEMORY_ALLOCATOR_DUMP_H_ - -#include <stdint.h> - -#include <memory> -#include <string> - -#include "base/base_export.h" -#include "base/gtest_prod_util.h" -#include "base/logging.h" -#include "base/macros.h" -#include "base/trace_event/memory_allocator_dump_guid.h" -#include "base/values.h" - -namespace base { -namespace trace_event { - -class ProcessMemoryDump; -class TracedValue; - -// Data model for user-land memory allocator dumps. -class BASE_EXPORT MemoryAllocatorDump { - public: - enum Flags { - DEFAULT = 0, - - // A dump marked weak will be discarded by TraceViewer. - WEAK = 1 << 0, - }; - - // MemoryAllocatorDump is owned by ProcessMemoryDump. - MemoryAllocatorDump(const std::string& absolute_name, - ProcessMemoryDump* process_memory_dump, - const MemoryAllocatorDumpGuid& guid); - MemoryAllocatorDump(const std::string& absolute_name, - ProcessMemoryDump* process_memory_dump); - ~MemoryAllocatorDump(); - - // Standard attribute |name|s for the AddScalar and AddString() methods. - static const char kNameSize[]; // To represent allocated space. - static const char kNameObjectCount[]; // To represent number of objects. - - // Standard attribute |unit|s for the AddScalar and AddString() methods. - static const char kUnitsBytes[]; // Unit name to represent bytes. - static const char kUnitsObjects[]; // Unit name to represent #objects. - - // Constants used only internally and by tests. - static const char kTypeScalar[]; // Type name for scalar attributes. - static const char kTypeString[]; // Type name for string attributes. - - // Setters for scalar attributes. Some examples: - // - "size" column (all dumps are expected to have at least this one): - // AddScalar(kNameSize, kUnitsBytes, 1234); - // - Some extra-column reporting internal details of the subsystem: - // AddScalar("number_of_freelist_entires", kUnitsObjects, 42) - // - Other informational column (will not be auto-added in the UI) - // AddScalarF("kittens_ratio", "ratio", 42.0f) - void AddScalar(const char* name, const char* units, uint64_t value); - void AddScalarF(const char* name, const char* units, double value); - void AddString(const char* name, const char* units, const std::string& value); - - // Absolute name, unique within the scope of an entire ProcessMemoryDump. - const std::string& absolute_name() const { return absolute_name_; } - - // Called at trace generation time to populate the TracedValue. - void AsValueInto(TracedValue* value) const; - - // Use enum Flags to set values. - void set_flags(int flags) { flags_ |= flags; } - void clear_flags(int flags) { flags_ &= ~flags; } - int flags() { return flags_; } - - // |guid| is an optional global dump identifier, unique across all processes - // within the scope of a global dump. It is only required when using the - // graph APIs (see TODO_method_name) to express retention / suballocation or - // cross process sharing. See crbug.com/492102 for design docs. - // Subsequent MemoryAllocatorDump(s) with the same |absolute_name| are - // expected to have the same guid. - const MemoryAllocatorDumpGuid& guid() const { return guid_; } - - TracedValue* attributes_for_testing() const { return attributes_.get(); } - - private: - // TODO(hjd): Transitional until we send the full PMD. See crbug.com/704203 - friend class MemoryDumpManager; - FRIEND_TEST_ALL_PREFIXES(MemoryAllocatorDumpTest, GetSize); - - // Get the size for this dump. - // The size is the value set with AddScalar(kNameSize, kUnitsBytes, size); - // TODO(hjd): Transitional until we send the full PMD. See crbug.com/704203 - uint64_t GetSize() const { return size_; }; - - const std::string absolute_name_; - ProcessMemoryDump* const process_memory_dump_; // Not owned (PMD owns this). - std::unique_ptr<TracedValue> attributes_; - MemoryAllocatorDumpGuid guid_; - int flags_; // See enum Flags. - uint64_t size_; - - // A local buffer for Sprintf conversion on fastpath. Avoids allocating - // temporary strings on each AddScalar() call. - std::string string_conversion_buffer_; - - DISALLOW_COPY_AND_ASSIGN(MemoryAllocatorDump); -}; - -} // namespace trace_event -} // namespace base - -#endif // BASE_TRACE_EVENT_MEMORY_ALLOCATOR_DUMP_H_ diff --git a/base/trace_event/memory_allocator_dump_guid.cc b/base/trace_event/memory_allocator_dump_guid.cc deleted file mode 100644 index bf4389a4c7..0000000000 --- a/base/trace_event/memory_allocator_dump_guid.cc +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/trace_event/memory_allocator_dump_guid.h" - -#include "base/format_macros.h" -#include "base/sha1.h" -#include "base/strings/stringprintf.h" - -namespace base { -namespace trace_event { - -namespace { -uint64_t HashString(const std::string& str) { - uint64_t hash[(kSHA1Length + sizeof(uint64_t) - 1) / sizeof(uint64_t)] = {0}; - SHA1HashBytes(reinterpret_cast<const unsigned char*>(str.data()), str.size(), - reinterpret_cast<unsigned char*>(hash)); - return hash[0]; -} -} // namespace - -MemoryAllocatorDumpGuid::MemoryAllocatorDumpGuid(uint64_t guid) : guid_(guid) {} - -MemoryAllocatorDumpGuid::MemoryAllocatorDumpGuid() - : MemoryAllocatorDumpGuid(0u) { -} - -MemoryAllocatorDumpGuid::MemoryAllocatorDumpGuid(const std::string& guid_str) - : MemoryAllocatorDumpGuid(HashString(guid_str)) { -} - -std::string MemoryAllocatorDumpGuid::ToString() const { - return StringPrintf("%" PRIx64, guid_); -} - -} // namespace trace_event -} // namespace base diff --git a/base/trace_event/memory_allocator_dump_guid.h b/base/trace_event/memory_allocator_dump_guid.h deleted file mode 100644 index b6472c6612..0000000000 --- a/base/trace_event/memory_allocator_dump_guid.h +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_TRACE_EVENT_MEMORY_ALLOCATOR_DUMP_GUID_H_ -#define BASE_TRACE_EVENT_MEMORY_ALLOCATOR_DUMP_GUID_H_ - -#include <stdint.h> - -#include <string> - -#include "base/base_export.h" - -namespace base { -namespace trace_event { - -class BASE_EXPORT MemoryAllocatorDumpGuid { - public: - MemoryAllocatorDumpGuid(); - explicit MemoryAllocatorDumpGuid(uint64_t guid); - - // Utility ctor to hash a GUID if the caller prefers a string. The caller - // still has to ensure that |guid_str| is unique, per snapshot, within the - // global scope of all the traced processes. - explicit MemoryAllocatorDumpGuid(const std::string& guid_str); - - uint64_t ToUint64() const { return guid_; } - - // Returns a (hex-encoded) string representation of the guid. - std::string ToString() const; - - bool empty() const { return guid_ == 0u; } - - bool operator==(const MemoryAllocatorDumpGuid& other) const { - return guid_ == other.guid_; - } - - bool operator!=(const MemoryAllocatorDumpGuid& other) const { - return !(*this == other); - } - - private: - uint64_t guid_; - - // Deliberately copy-able. -}; - -} // namespace trace_event -} // namespace base - -#endif // BASE_TRACE_EVENT_MEMORY_ALLOCATOR_DUMP_GUID_H_ diff --git a/base/trace_event/memory_allocator_dump_unittest.cc b/base/trace_event/memory_allocator_dump_unittest.cc deleted file mode 100644 index e1818f6eec..0000000000 --- a/base/trace_event/memory_allocator_dump_unittest.cc +++ /dev/null @@ -1,200 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/trace_event/memory_allocator_dump.h" - -#include <stdint.h> - -#include "base/format_macros.h" -#include "base/strings/stringprintf.h" -#include "base/trace_event/memory_allocator_dump_guid.h" -#include "base/trace_event/memory_dump_provider.h" -#include "base/trace_event/memory_dump_session_state.h" -#include "base/trace_event/process_memory_dump.h" -#include "base/trace_event/trace_event_argument.h" -#include "base/values.h" -#include "build/build_config.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { -namespace trace_event { - -namespace { - -class FakeMemoryAllocatorDumpProvider : public MemoryDumpProvider { - public: - bool OnMemoryDump(const MemoryDumpArgs& args, - ProcessMemoryDump* pmd) override { - MemoryAllocatorDump* root_heap = - pmd->CreateAllocatorDump("foobar_allocator"); - - root_heap->AddScalar(MemoryAllocatorDump::kNameSize, - MemoryAllocatorDump::kUnitsBytes, 4096); - root_heap->AddScalar(MemoryAllocatorDump::kNameObjectCount, - MemoryAllocatorDump::kUnitsObjects, 42); - root_heap->AddScalar("attr1", "units1", 1234); - root_heap->AddString("attr2", "units2", "string_value"); - root_heap->AddScalarF("attr3", "units3", 42.5f); - - MemoryAllocatorDump* sub_heap = - pmd->CreateAllocatorDump("foobar_allocator/sub_heap"); - sub_heap->AddScalar(MemoryAllocatorDump::kNameSize, - MemoryAllocatorDump::kUnitsBytes, 1); - sub_heap->AddScalar(MemoryAllocatorDump::kNameObjectCount, - MemoryAllocatorDump::kUnitsObjects, 3); - - pmd->CreateAllocatorDump("foobar_allocator/sub_heap/empty"); - // Leave the rest of sub heap deliberately uninitialized, to check that - // CreateAllocatorDump returns a properly zero-initialized object. - - return true; - } -}; - -std::unique_ptr<Value> CheckAttribute(const MemoryAllocatorDump* dump, - const std::string& name, - const char* expected_type, - const char* expected_units) { - std::unique_ptr<Value> raw_attrs = - dump->attributes_for_testing()->ToBaseValue(); - DictionaryValue* args = nullptr; - DictionaryValue* arg = nullptr; - std::string arg_value; - const Value* out_value = nullptr; - EXPECT_TRUE(raw_attrs->GetAsDictionary(&args)); - EXPECT_TRUE(args->GetDictionary(name, &arg)); - EXPECT_TRUE(arg->GetString("type", &arg_value)); - EXPECT_EQ(expected_type, arg_value); - EXPECT_TRUE(arg->GetString("units", &arg_value)); - EXPECT_EQ(expected_units, arg_value); - EXPECT_TRUE(arg->Get("value", &out_value)); - return out_value ? out_value->CreateDeepCopy() : std::unique_ptr<Value>(); -} - -void CheckString(const MemoryAllocatorDump* dump, - const std::string& name, - const char* expected_type, - const char* expected_units, - const std::string& expected_value) { - std::string attr_str_value; - auto attr_value = CheckAttribute(dump, name, expected_type, expected_units); - EXPECT_TRUE(attr_value->GetAsString(&attr_str_value)); - EXPECT_EQ(expected_value, attr_str_value); -} - -void CheckScalar(const MemoryAllocatorDump* dump, - const std::string& name, - const char* expected_units, - uint64_t expected_value) { - CheckString(dump, name, MemoryAllocatorDump::kTypeScalar, expected_units, - StringPrintf("%" PRIx64, expected_value)); -} - -void CheckScalarF(const MemoryAllocatorDump* dump, - const std::string& name, - const char* expected_units, - double expected_value) { - auto attr_value = CheckAttribute(dump, name, MemoryAllocatorDump::kTypeScalar, - expected_units); - double attr_double_value; - EXPECT_TRUE(attr_value->GetAsDouble(&attr_double_value)); - EXPECT_EQ(expected_value, attr_double_value); -} - -} // namespace - -TEST(MemoryAllocatorDumpTest, GuidGeneration) { - std::unique_ptr<MemoryAllocatorDump> mad( - new MemoryAllocatorDump("foo", nullptr, MemoryAllocatorDumpGuid(0x42u))); - ASSERT_EQ("42", mad->guid().ToString()); - - // If the dumper does not provide a Guid, the MAD will make one up on the - // flight. Furthermore that Guid will stay stable across across multiple - // snapshots if the |absolute_name| of the dump doesn't change - mad.reset(new MemoryAllocatorDump("bar", nullptr)); - const MemoryAllocatorDumpGuid guid_bar = mad->guid(); - ASSERT_FALSE(guid_bar.empty()); - ASSERT_FALSE(guid_bar.ToString().empty()); - ASSERT_EQ(guid_bar, mad->guid()); - - mad.reset(new MemoryAllocatorDump("bar", nullptr)); - const MemoryAllocatorDumpGuid guid_bar_2 = mad->guid(); - ASSERT_EQ(guid_bar, guid_bar_2); - - mad.reset(new MemoryAllocatorDump("baz", nullptr)); - const MemoryAllocatorDumpGuid guid_baz = mad->guid(); - ASSERT_NE(guid_bar, guid_baz); -} - -TEST(MemoryAllocatorDumpTest, DumpIntoProcessMemoryDump) { - FakeMemoryAllocatorDumpProvider fmadp; - MemoryDumpArgs dump_args = {MemoryDumpLevelOfDetail::DETAILED}; - ProcessMemoryDump pmd(new MemoryDumpSessionState, dump_args); - - fmadp.OnMemoryDump(dump_args, &pmd); - - ASSERT_EQ(3u, pmd.allocator_dumps().size()); - - const MemoryAllocatorDump* root_heap = - pmd.GetAllocatorDump("foobar_allocator"); - ASSERT_NE(nullptr, root_heap); - EXPECT_EQ("foobar_allocator", root_heap->absolute_name()); - CheckScalar(root_heap, MemoryAllocatorDump::kNameSize, - MemoryAllocatorDump::kUnitsBytes, 4096); - CheckScalar(root_heap, MemoryAllocatorDump::kNameObjectCount, - MemoryAllocatorDump::kUnitsObjects, 42); - CheckScalar(root_heap, "attr1", "units1", 1234); - CheckString(root_heap, "attr2", MemoryAllocatorDump::kTypeString, "units2", - "string_value"); - CheckScalarF(root_heap, "attr3", "units3", 42.5f); - - const MemoryAllocatorDump* sub_heap = - pmd.GetAllocatorDump("foobar_allocator/sub_heap"); - ASSERT_NE(nullptr, sub_heap); - EXPECT_EQ("foobar_allocator/sub_heap", sub_heap->absolute_name()); - CheckScalar(sub_heap, MemoryAllocatorDump::kNameSize, - MemoryAllocatorDump::kUnitsBytes, 1); - CheckScalar(sub_heap, MemoryAllocatorDump::kNameObjectCount, - MemoryAllocatorDump::kUnitsObjects, 3); - const MemoryAllocatorDump* empty_sub_heap = - pmd.GetAllocatorDump("foobar_allocator/sub_heap/empty"); - ASSERT_NE(nullptr, empty_sub_heap); - EXPECT_EQ("foobar_allocator/sub_heap/empty", empty_sub_heap->absolute_name()); - auto raw_attrs = empty_sub_heap->attributes_for_testing()->ToBaseValue(); - DictionaryValue* attrs = nullptr; - ASSERT_TRUE(raw_attrs->GetAsDictionary(&attrs)); - ASSERT_FALSE(attrs->HasKey(MemoryAllocatorDump::kNameSize)); - ASSERT_FALSE(attrs->HasKey(MemoryAllocatorDump::kNameObjectCount)); - - // Check that the AsValueInfo doesn't hit any DCHECK. - std::unique_ptr<TracedValue> traced_value(new TracedValue); - pmd.AsValueInto(traced_value.get()); -} - -TEST(MemoryAllocatorDumpTest, GetSize) { - MemoryDumpArgs dump_args = {MemoryDumpLevelOfDetail::DETAILED}; - ProcessMemoryDump pmd(new MemoryDumpSessionState, dump_args); - MemoryAllocatorDump* dump = pmd.CreateAllocatorDump("allocator_for_size"); - dump->AddScalar(MemoryAllocatorDump::kNameSize, - MemoryAllocatorDump::kUnitsBytes, 1); - dump->AddScalar("foo", MemoryAllocatorDump::kUnitsBytes, 2); - EXPECT_EQ(1u, dump->GetSize()); -} - -// DEATH tests are not supported in Android / iOS. -#if !defined(NDEBUG) && !defined(OS_ANDROID) && !defined(OS_IOS) -TEST(MemoryAllocatorDumpTest, ForbidDuplicatesDeathTest) { - FakeMemoryAllocatorDumpProvider fmadp; - MemoryDumpArgs dump_args = {MemoryDumpLevelOfDetail::DETAILED}; - ProcessMemoryDump pmd(new MemoryDumpSessionState, dump_args); - pmd.CreateAllocatorDump("foo_allocator"); - pmd.CreateAllocatorDump("bar_allocator/heap"); - ASSERT_DEATH(pmd.CreateAllocatorDump("foo_allocator"), ""); - ASSERT_DEATH(pmd.CreateAllocatorDump("bar_allocator/heap"), ""); - ASSERT_DEATH(pmd.CreateAllocatorDump(""), ""); -} -#endif - -} // namespace trace_event -} // namespace base diff --git a/base/trace_event/memory_dump_manager.cc b/base/trace_event/memory_dump_manager.cc deleted file mode 100644 index 6ed1ca8fff..0000000000 --- a/base/trace_event/memory_dump_manager.cc +++ /dev/null @@ -1,991 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/trace_event/memory_dump_manager.h" - -#include <inttypes.h> -#include <stdio.h> - -#include <algorithm> -#include <utility> - -#include "base/allocator/features.h" -#include "base/atomic_sequence_num.h" -#include "base/base_switches.h" -#include "base/command_line.h" -#include "base/compiler_specific.h" -#include "base/debug/alias.h" -#include "base/debug/debugging_flags.h" -#include "base/debug/stack_trace.h" -#include "base/debug/thread_heap_usage_tracker.h" -#include "base/memory/ptr_util.h" -#include "base/strings/pattern.h" -#include "base/strings/string_piece.h" -#include "base/threading/thread.h" -#include "base/threading/thread_task_runner_handle.h" -#include "base/trace_event/heap_profiler.h" -#include "base/trace_event/heap_profiler_allocation_context_tracker.h" -#include "base/trace_event/heap_profiler_event_filter.h" -#include "base/trace_event/heap_profiler_stack_frame_deduplicator.h" -#include "base/trace_event/heap_profiler_type_name_deduplicator.h" -#include "base/trace_event/malloc_dump_provider.h" -#include "base/trace_event/memory_dump_provider.h" -#include "base/trace_event/memory_dump_scheduler.h" -#include "base/trace_event/memory_dump_session_state.h" -#include "base/trace_event/memory_infra_background_whitelist.h" -#include "base/trace_event/process_memory_dump.h" -#include "base/trace_event/trace_event.h" -#include "base/trace_event/trace_event_argument.h" -#include "build/build_config.h" - -#if defined(OS_ANDROID) -#include "base/trace_event/java_heap_dump_provider_android.h" -#endif - -namespace base { -namespace trace_event { - -namespace { - -const int kTraceEventNumArgs = 1; -const char* kTraceEventArgNames[] = {"dumps"}; -const unsigned char kTraceEventArgTypes[] = {TRACE_VALUE_TYPE_CONVERTABLE}; - -StaticAtomicSequenceNumber g_next_guid; -MemoryDumpManager* g_instance_for_testing = nullptr; - -// The list of names of dump providers that are blacklisted from strict thread -// affinity check on unregistration. These providers could potentially cause -// crashes on build bots if they do not unregister on right thread. -// TODO(ssid): Fix all the dump providers to unregister if needed and clear the -// blacklist, crbug.com/643438. -const char* const kStrictThreadCheckBlacklist[] = { - "ClientDiscardableSharedMemoryManager", - "ContextProviderCommandBuffer", - "DiscardableSharedMemoryManager", - "FontCaches", - "GpuMemoryBufferVideoFramePool", - "IndexedDBBackingStore", - "Sql", - "ThreadLocalEventBuffer", - "TraceLog", - "URLRequestContext", - "VpxVideoDecoder", - "cc::SoftwareImageDecodeCache", - "cc::StagingBufferPool", - "gpu::BufferManager", - "gpu::MappedMemoryManager", - "gpu::RenderbufferManager", - "BlacklistTestDumpProvider" // for testing -}; - -// Callback wrapper to hook upon the completion of RequestGlobalDump() and -// inject trace markers. -void OnGlobalDumpDone(MemoryDumpCallback wrapped_callback, - uint64_t dump_guid, - bool success) { - char guid_str[20]; - sprintf(guid_str, "0x%" PRIx64, dump_guid); - TRACE_EVENT_NESTABLE_ASYNC_END2(MemoryDumpManager::kTraceCategory, - "GlobalMemoryDump", TRACE_ID_LOCAL(dump_guid), - "dump_guid", TRACE_STR_COPY(guid_str), - "success", success); - - if (!wrapped_callback.is_null()) { - wrapped_callback.Run(dump_guid, success); - wrapped_callback.Reset(); - } -} - -// Proxy class which wraps a ConvertableToTraceFormat owned by the -// |session_state| into a proxy object that can be added to the trace event log. -// This is to solve the problem that the MemoryDumpSessionState is refcounted -// but the tracing subsystem wants a std::unique_ptr<ConvertableToTraceFormat>. -template <typename T> -struct SessionStateConvertableProxy : public ConvertableToTraceFormat { - using GetterFunctPtr = T* (MemoryDumpSessionState::*)() const; - - SessionStateConvertableProxy( - scoped_refptr<MemoryDumpSessionState> session_state, - GetterFunctPtr getter_function) - : session_state(session_state), getter_function(getter_function) {} - - void AppendAsTraceFormat(std::string* out) const override { - return (session_state.get()->*getter_function)()->AppendAsTraceFormat(out); - } - - void EstimateTraceMemoryOverhead( - TraceEventMemoryOverhead* overhead) override { - return (session_state.get()->*getter_function)() - ->EstimateTraceMemoryOverhead(overhead); - } - - scoped_refptr<MemoryDumpSessionState> session_state; - GetterFunctPtr const getter_function; -}; - -} // namespace - -// static -const char* const MemoryDumpManager::kTraceCategory = - TRACE_DISABLED_BY_DEFAULT("memory-infra"); - -// static -const char* const MemoryDumpManager::kLogPrefix = "Memory-infra dump"; - -// static -const int MemoryDumpManager::kMaxConsecutiveFailuresCount = 3; - -// static -const uint64_t MemoryDumpManager::kInvalidTracingProcessId = 0; - -// static -const char* const MemoryDumpManager::kSystemAllocatorPoolName = -#if defined(MALLOC_MEMORY_TRACING_SUPPORTED) - MallocDumpProvider::kAllocatedObjects; -#else - nullptr; -#endif - -// static -MemoryDumpManager* MemoryDumpManager::GetInstance() { - if (g_instance_for_testing) - return g_instance_for_testing; - - return Singleton<MemoryDumpManager, - LeakySingletonTraits<MemoryDumpManager>>::get(); -} - -// static -void MemoryDumpManager::SetInstanceForTesting(MemoryDumpManager* instance) { - g_instance_for_testing = instance; -} - -MemoryDumpManager::MemoryDumpManager() - : memory_tracing_enabled_(0), - tracing_process_id_(kInvalidTracingProcessId), - dumper_registrations_ignored_for_testing_(false), - heap_profiling_enabled_(false) { - g_next_guid.GetNext(); // Make sure that first guid is not zero. - - // At this point the command line may not be initialized but we try to - // enable the heap profiler to capture allocations as soon as possible. - EnableHeapProfilingIfNeeded(); - - strict_thread_check_blacklist_.insert(std::begin(kStrictThreadCheckBlacklist), - std::end(kStrictThreadCheckBlacklist)); -} - -MemoryDumpManager::~MemoryDumpManager() { - TraceLog::GetInstance()->RemoveEnabledStateObserver(this); -} - -void MemoryDumpManager::EnableHeapProfilingIfNeeded() { - if (heap_profiling_enabled_) - return; - - if (!CommandLine::InitializedForCurrentProcess() || - !CommandLine::ForCurrentProcess()->HasSwitch( - switches::kEnableHeapProfiling)) - return; - - std::string profiling_mode = CommandLine::ForCurrentProcess() - ->GetSwitchValueASCII(switches::kEnableHeapProfiling); - if (profiling_mode == "") { - AllocationContextTracker::SetCaptureMode( - AllocationContextTracker::CaptureMode::PSEUDO_STACK); -#if HAVE_TRACE_STACK_FRAME_POINTERS && \ - (BUILDFLAG(ENABLE_PROFILING) || !defined(NDEBUG)) - } else if (profiling_mode == switches::kEnableHeapProfilingModeNative) { - // We need frame pointers for native tracing to work, and they are - // enabled in profiling and debug builds. - AllocationContextTracker::SetCaptureMode( - AllocationContextTracker::CaptureMode::NATIVE_STACK); -#endif -#if BUILDFLAG(ENABLE_MEMORY_TASK_PROFILER) - } else if (profiling_mode == switches::kEnableHeapProfilingTaskProfiler) { - // Enable heap tracking, which in turn enables capture of heap usage - // tracking in tracked_objects.cc. - if (!base::debug::ThreadHeapUsageTracker::IsHeapTrackingEnabled()) - base::debug::ThreadHeapUsageTracker::EnableHeapTracking(); -#endif - } else { - CHECK(false) << "Invalid mode '" << profiling_mode << "' for " - << switches::kEnableHeapProfiling << " flag."; - } - - for (auto mdp : dump_providers_) - mdp->dump_provider->OnHeapProfilingEnabled(true); - heap_profiling_enabled_ = true; -} - -void MemoryDumpManager::Initialize( - std::unique_ptr<MemoryDumpManagerDelegate> delegate) { - { - AutoLock lock(lock_); - DCHECK(delegate); - DCHECK(!delegate_); - delegate_ = std::move(delegate); - EnableHeapProfilingIfNeeded(); - } - -// Enable the core dump providers. -#if defined(MALLOC_MEMORY_TRACING_SUPPORTED) - RegisterDumpProvider(MallocDumpProvider::GetInstance(), "Malloc", nullptr); -#endif - -#if defined(OS_ANDROID) - RegisterDumpProvider(JavaHeapDumpProvider::GetInstance(), "JavaHeap", - nullptr); -#endif - - TRACE_EVENT_WARMUP_CATEGORY(kTraceCategory); - - // TODO(ssid): This should be done in EnableHeapProfiling so that we capture - // more allocations (crbug.com/625170). - if (AllocationContextTracker::capture_mode() == - AllocationContextTracker::CaptureMode::PSEUDO_STACK && - !(TraceLog::GetInstance()->enabled_modes() & TraceLog::FILTERING_MODE)) { - // Create trace config with heap profiling filter. - std::string filter_string = "*"; - const char* const kFilteredCategories[] = { - TRACE_DISABLED_BY_DEFAULT("net"), TRACE_DISABLED_BY_DEFAULT("cc"), - MemoryDumpManager::kTraceCategory}; - for (const char* cat : kFilteredCategories) - filter_string = filter_string + "," + cat; - TraceConfigCategoryFilter category_filter; - category_filter.InitializeFromString(filter_string); - - TraceConfig::EventFilterConfig heap_profiler_filter_config( - HeapProfilerEventFilter::kName); - heap_profiler_filter_config.SetCategoryFilter(category_filter); - - TraceConfig::EventFilters filters; - filters.push_back(heap_profiler_filter_config); - TraceConfig filtering_trace_config; - filtering_trace_config.SetEventFilters(filters); - - TraceLog::GetInstance()->SetEnabled(filtering_trace_config, - TraceLog::FILTERING_MODE); - } - - // If tracing was enabled before initializing MemoryDumpManager, we missed the - // OnTraceLogEnabled() event. Synthetize it so we can late-join the party. - // IsEnabled is called before adding observer to avoid calling - // OnTraceLogEnabled twice. - bool is_tracing_already_enabled = TraceLog::GetInstance()->IsEnabled(); - TraceLog::GetInstance()->AddEnabledStateObserver(this); - if (is_tracing_already_enabled) - OnTraceLogEnabled(); -} - -void MemoryDumpManager::RegisterDumpProvider( - MemoryDumpProvider* mdp, - const char* name, - scoped_refptr<SingleThreadTaskRunner> task_runner, - MemoryDumpProvider::Options options) { - options.dumps_on_single_thread_task_runner = true; - RegisterDumpProviderInternal(mdp, name, std::move(task_runner), options); -} - -void MemoryDumpManager::RegisterDumpProvider( - MemoryDumpProvider* mdp, - const char* name, - scoped_refptr<SingleThreadTaskRunner> task_runner) { - // Set |dumps_on_single_thread_task_runner| to true because all providers - // without task runner are run on dump thread. - MemoryDumpProvider::Options options; - options.dumps_on_single_thread_task_runner = true; - RegisterDumpProviderInternal(mdp, name, std::move(task_runner), options); -} - -void MemoryDumpManager::RegisterDumpProviderWithSequencedTaskRunner( - MemoryDumpProvider* mdp, - const char* name, - scoped_refptr<SequencedTaskRunner> task_runner, - MemoryDumpProvider::Options options) { - DCHECK(task_runner); - options.dumps_on_single_thread_task_runner = false; - RegisterDumpProviderInternal(mdp, name, std::move(task_runner), options); -} - -void MemoryDumpManager::RegisterDumpProviderInternal( - MemoryDumpProvider* mdp, - const char* name, - scoped_refptr<SequencedTaskRunner> task_runner, - const MemoryDumpProvider::Options& options) { - if (dumper_registrations_ignored_for_testing_) - return; - - bool whitelisted_for_background_mode = IsMemoryDumpProviderWhitelisted(name); - scoped_refptr<MemoryDumpProviderInfo> mdpinfo = - new MemoryDumpProviderInfo(mdp, name, std::move(task_runner), options, - whitelisted_for_background_mode); - - if (options.is_fast_polling_supported) { - DCHECK(!mdpinfo->task_runner) << "MemoryDumpProviders capable of fast " - "polling must NOT be thread bound."; - } - - { - AutoLock lock(lock_); - bool already_registered = !dump_providers_.insert(mdpinfo).second; - // This actually happens in some tests which don't have a clean tear-down - // path for RenderThreadImpl::Init(). - if (already_registered) - return; - - // The list of polling MDPs is populated OnTraceLogEnabled(). This code - // deals with the case of a MDP capable of fast polling that is registered - // after the OnTraceLogEnabled() - if (options.is_fast_polling_supported && dump_thread_) { - dump_thread_->task_runner()->PostTask( - FROM_HERE, Bind(&MemoryDumpManager::RegisterPollingMDPOnDumpThread, - Unretained(this), mdpinfo)); - } - } - - if (heap_profiling_enabled_) - mdp->OnHeapProfilingEnabled(true); -} - -void MemoryDumpManager::UnregisterDumpProvider(MemoryDumpProvider* mdp) { - UnregisterDumpProviderInternal(mdp, false /* delete_async */); -} - -void MemoryDumpManager::UnregisterAndDeleteDumpProviderSoon( - std::unique_ptr<MemoryDumpProvider> mdp) { - UnregisterDumpProviderInternal(mdp.release(), true /* delete_async */); -} - -void MemoryDumpManager::UnregisterDumpProviderInternal( - MemoryDumpProvider* mdp, - bool take_mdp_ownership_and_delete_async) { - std::unique_ptr<MemoryDumpProvider> owned_mdp; - if (take_mdp_ownership_and_delete_async) - owned_mdp.reset(mdp); - - AutoLock lock(lock_); - - auto mdp_iter = dump_providers_.begin(); - for (; mdp_iter != dump_providers_.end(); ++mdp_iter) { - if ((*mdp_iter)->dump_provider == mdp) - break; - } - - if (mdp_iter == dump_providers_.end()) - return; // Not registered / already unregistered. - - if (take_mdp_ownership_and_delete_async) { - // The MDP will be deleted whenever the MDPInfo struct will, that is either: - // - At the end of this function, if no dump is in progress. - // - Either in SetupNextMemoryDump() or InvokeOnMemoryDump() when MDPInfo is - // removed from |pending_dump_providers|. - // - When the provider is removed from |dump_providers_for_polling_|. - DCHECK(!(*mdp_iter)->owned_dump_provider); - (*mdp_iter)->owned_dump_provider = std::move(owned_mdp); - } else if (strict_thread_check_blacklist_.count((*mdp_iter)->name) == 0 || - subtle::NoBarrier_Load(&memory_tracing_enabled_)) { - // If dump provider's name is on |strict_thread_check_blacklist_|, then the - // DCHECK is fired only when tracing is enabled. Otherwise the DCHECK is - // fired even when tracing is not enabled (stricter). - // TODO(ssid): Remove this condition after removing all the dump providers - // in the blacklist and the buildbots are no longer flakily hitting the - // DCHECK, crbug.com/643438. - - // If you hit this DCHECK, your dump provider has a bug. - // Unregistration of a MemoryDumpProvider is safe only if: - // - The MDP has specified a sequenced task runner affinity AND the - // unregistration happens on the same task runner. So that the MDP cannot - // unregister and be in the middle of a OnMemoryDump() at the same time. - // - The MDP has NOT specified a task runner affinity and its ownership is - // transferred via UnregisterAndDeleteDumpProviderSoon(). - // In all the other cases, it is not possible to guarantee that the - // unregistration will not race with OnMemoryDump() calls. - DCHECK((*mdp_iter)->task_runner && - (*mdp_iter)->task_runner->RunsTasksOnCurrentThread()) - << "MemoryDumpProvider \"" << (*mdp_iter)->name << "\" attempted to " - << "unregister itself in a racy way. Please file a crbug."; - } - - if ((*mdp_iter)->options.is_fast_polling_supported && dump_thread_) { - DCHECK(take_mdp_ownership_and_delete_async); - dump_thread_->task_runner()->PostTask( - FROM_HERE, Bind(&MemoryDumpManager::UnregisterPollingMDPOnDumpThread, - Unretained(this), *mdp_iter)); - } - - // The MDPInfo instance can still be referenced by the - // |ProcessMemoryDumpAsyncState.pending_dump_providers|. For this reason - // the MDPInfo is flagged as disabled. It will cause InvokeOnMemoryDump() - // to just skip it, without actually invoking the |mdp|, which might be - // destroyed by the caller soon after this method returns. - (*mdp_iter)->disabled = true; - dump_providers_.erase(mdp_iter); -} - -void MemoryDumpManager::RegisterPollingMDPOnDumpThread( - scoped_refptr<MemoryDumpProviderInfo> mdpinfo) { - AutoLock lock(lock_); - dump_providers_for_polling_.insert(mdpinfo); - - // Notify ready for polling when first polling supported provider is - // registered. This handles the case where OnTraceLogEnabled() did not notify - // ready since no polling supported mdp has yet been registered. - if (dump_providers_for_polling_.size() == 1) - MemoryDumpScheduler::GetInstance()->EnablePollingIfNeeded(); -} - -void MemoryDumpManager::UnregisterPollingMDPOnDumpThread( - scoped_refptr<MemoryDumpProviderInfo> mdpinfo) { - mdpinfo->dump_provider->SuspendFastMemoryPolling(); - - AutoLock lock(lock_); - dump_providers_for_polling_.erase(mdpinfo); - DCHECK(!dump_providers_for_polling_.empty()) - << "All polling MDPs cannot be unregistered."; -} - -void MemoryDumpManager::RequestGlobalDump( - MemoryDumpType dump_type, - MemoryDumpLevelOfDetail level_of_detail, - const MemoryDumpCallback& callback) { - // Bail out immediately if tracing is not enabled at all or if the dump mode - // is not allowed. - if (!UNLIKELY(subtle::NoBarrier_Load(&memory_tracing_enabled_)) || - !IsDumpModeAllowed(level_of_detail)) { - VLOG(1) << kLogPrefix << " failed because " << kTraceCategory - << " tracing category is not enabled or the requested dump mode is " - "not allowed by trace config."; - if (!callback.is_null()) - callback.Run(0u /* guid */, false /* success */); - return; - } - - const uint64_t guid = - TraceLog::GetInstance()->MangleEventId(g_next_guid.GetNext()); - - // Creates an async event to keep track of the global dump evolution. - // The |wrapped_callback| will generate the ASYNC_END event and then invoke - // the real |callback| provided by the caller. - TRACE_EVENT_NESTABLE_ASYNC_BEGIN2( - kTraceCategory, "GlobalMemoryDump", TRACE_ID_LOCAL(guid), "dump_type", - MemoryDumpTypeToString(dump_type), "level_of_detail", - MemoryDumpLevelOfDetailToString(level_of_detail)); - MemoryDumpCallback wrapped_callback = Bind(&OnGlobalDumpDone, callback); - - // The delegate will coordinate the IPC broadcast and at some point invoke - // CreateProcessDump() to get a dump for the current process. - MemoryDumpRequestArgs args = {guid, dump_type, level_of_detail}; - delegate_->RequestGlobalMemoryDump(args, wrapped_callback); -} - -void MemoryDumpManager::RequestGlobalDump( - MemoryDumpType dump_type, - MemoryDumpLevelOfDetail level_of_detail) { - RequestGlobalDump(dump_type, level_of_detail, MemoryDumpCallback()); -} - -bool MemoryDumpManager::IsDumpProviderRegisteredForTesting( - MemoryDumpProvider* provider) { - AutoLock lock(lock_); - - for (const auto& info : dump_providers_) { - if (info->dump_provider == provider) - return true; - } - return false; -} - -void MemoryDumpManager::CreateProcessDump(const MemoryDumpRequestArgs& args, - const MemoryDumpCallback& callback) { - char guid_str[20]; - sprintf(guid_str, "0x%" PRIx64, args.dump_guid); - TRACE_EVENT_NESTABLE_ASYNC_BEGIN1(kTraceCategory, "ProcessMemoryDump", - TRACE_ID_LOCAL(args.dump_guid), "dump_guid", - TRACE_STR_COPY(guid_str)); - - // If argument filter is enabled then only background mode dumps should be - // allowed. In case the trace config passed for background tracing session - // missed the allowed modes argument, it crashes here instead of creating - // unexpected dumps. - if (TraceLog::GetInstance() - ->GetCurrentTraceConfig() - .IsArgumentFilterEnabled()) { - CHECK_EQ(MemoryDumpLevelOfDetail::BACKGROUND, args.level_of_detail); - } - - std::unique_ptr<ProcessMemoryDumpAsyncState> pmd_async_state; - { - AutoLock lock(lock_); - - // |dump_thread_| can be nullptr is tracing was disabled before reaching - // here. SetupNextMemoryDump() is robust enough to tolerate it and will - // NACK the dump. - pmd_async_state.reset(new ProcessMemoryDumpAsyncState( - args, dump_providers_, session_state_, callback, - dump_thread_ ? dump_thread_->task_runner() : nullptr)); - - // Safety check to prevent reaching here without calling RequestGlobalDump, - // with disallowed modes. If |session_state_| is null then tracing is - // disabled. - CHECK(!session_state_ || - session_state_->IsDumpModeAllowed(args.level_of_detail)); - - MemoryDumpScheduler::GetInstance()->NotifyDumpTriggered(); - } - - // Start the process dump. This involves task runner hops as specified by the - // MemoryDumpProvider(s) in RegisterDumpProvider()). - SetupNextMemoryDump(std::move(pmd_async_state)); -} - -// PostTask InvokeOnMemoryDump() to the dump provider's sequenced task runner. A -// PostTask is always required for a generic SequencedTaskRunner to ensure that -// no other task is running on it concurrently. SetupNextMemoryDump() and -// InvokeOnMemoryDump() are called alternatively which linearizes the dump -// provider's OnMemoryDump invocations. -// At most one of either SetupNextMemoryDump() or InvokeOnMemoryDump() can be -// active at any time for a given PMD, regardless of status of the |lock_|. -// |lock_| is used in these functions purely to ensure consistency w.r.t. -// (un)registrations of |dump_providers_|. -void MemoryDumpManager::SetupNextMemoryDump( - std::unique_ptr<ProcessMemoryDumpAsyncState> pmd_async_state) { - HEAP_PROFILER_SCOPED_IGNORE; - // Initalizes the ThreadLocalEventBuffer to guarantee that the TRACE_EVENTs - // in the PostTask below don't end up registering their own dump providers - // (for discounting trace memory overhead) while holding the |lock_|. - TraceLog::GetInstance()->InitializeThreadLocalEventBufferIfSupported(); - - // |dump_thread_| might be destroyed before getting this point. - // It means that tracing was disabled right before starting this dump. - // Anyway either tracing is stopped or this was the last hop, create a trace - // event, add it to the trace and finalize process dump invoking the callback. - if (!pmd_async_state->dump_thread_task_runner.get()) { - if (pmd_async_state->pending_dump_providers.empty()) { - VLOG(1) << kLogPrefix << " failed because dump thread was destroyed" - << " before finalizing the dump"; - } else { - VLOG(1) << kLogPrefix << " failed because dump thread was destroyed" - << " before dumping " - << pmd_async_state->pending_dump_providers.back().get()->name; - } - pmd_async_state->dump_successful = false; - pmd_async_state->pending_dump_providers.clear(); - } - if (pmd_async_state->pending_dump_providers.empty()) - return FinalizeDumpAndAddToTrace(std::move(pmd_async_state)); - - // Read MemoryDumpProviderInfo thread safety considerations in - // memory_dump_manager.h when accessing |mdpinfo| fields. - MemoryDumpProviderInfo* mdpinfo = - pmd_async_state->pending_dump_providers.back().get(); - - // If we are in background tracing, we should invoke only the whitelisted - // providers. Ignore other providers and continue. - if (pmd_async_state->req_args.level_of_detail == - MemoryDumpLevelOfDetail::BACKGROUND && - !mdpinfo->whitelisted_for_background_mode) { - pmd_async_state->pending_dump_providers.pop_back(); - return SetupNextMemoryDump(std::move(pmd_async_state)); - } - - // If the dump provider did not specify a task runner affinity, dump on - // |dump_thread_| which is already checked above for presence. - SequencedTaskRunner* task_runner = mdpinfo->task_runner.get(); - if (!task_runner) { - DCHECK(mdpinfo->options.dumps_on_single_thread_task_runner); - task_runner = pmd_async_state->dump_thread_task_runner.get(); - DCHECK(task_runner); - } - - if (mdpinfo->options.dumps_on_single_thread_task_runner && - task_runner->RunsTasksOnCurrentThread()) { - // If |dumps_on_single_thread_task_runner| is true then no PostTask is - // required if we are on the right thread. - return InvokeOnMemoryDump(pmd_async_state.release()); - } - - bool did_post_task = task_runner->PostTask( - FROM_HERE, Bind(&MemoryDumpManager::InvokeOnMemoryDump, Unretained(this), - Unretained(pmd_async_state.get()))); - - if (did_post_task) { - // Ownership is tranferred to InvokeOnMemoryDump(). - ignore_result(pmd_async_state.release()); - return; - } - - // PostTask usually fails only if the process or thread is shut down. So, the - // dump provider is disabled here. But, don't disable unbound dump providers. - // The utility thread is normally shutdown when disabling the trace and - // getting here in this case is expected. - if (mdpinfo->task_runner) { - LOG(ERROR) << "Disabling MemoryDumpProvider \"" << mdpinfo->name - << "\". Failed to post task on the task runner provided."; - - // A locked access is required to R/W |disabled| (for the - // UnregisterAndDeleteDumpProviderSoon() case). - AutoLock lock(lock_); - mdpinfo->disabled = true; - } - - // PostTask failed. Ignore the dump provider and continue. - pmd_async_state->pending_dump_providers.pop_back(); - SetupNextMemoryDump(std::move(pmd_async_state)); -} - -// This function is called on the right task runner for current MDP. It is -// either the task runner specified by MDP or |dump_thread_task_runner| if the -// MDP did not specify task runner. Invokes the dump provider's OnMemoryDump() -// (unless disabled). -void MemoryDumpManager::InvokeOnMemoryDump( - ProcessMemoryDumpAsyncState* owned_pmd_async_state) { - HEAP_PROFILER_SCOPED_IGNORE; - // In theory |owned_pmd_async_state| should be a scoped_ptr. The only reason - // why it isn't is because of the corner case logic of |did_post_task| - // above, which needs to take back the ownership of the |pmd_async_state| when - // the PostTask() fails. - // Unfortunately, PostTask() destroys the scoped_ptr arguments upon failure - // to prevent accidental leaks. Using a scoped_ptr would prevent us to to - // skip the hop and move on. Hence the manual naked -> scoped ptr juggling. - auto pmd_async_state = WrapUnique(owned_pmd_async_state); - owned_pmd_async_state = nullptr; - - // Read MemoryDumpProviderInfo thread safety considerations in - // memory_dump_manager.h when accessing |mdpinfo| fields. - MemoryDumpProviderInfo* mdpinfo = - pmd_async_state->pending_dump_providers.back().get(); - - DCHECK(!mdpinfo->task_runner || - mdpinfo->task_runner->RunsTasksOnCurrentThread()); - - bool should_dump; - { - // A locked access is required to R/W |disabled| (for the - // UnregisterAndDeleteDumpProviderSoon() case). - AutoLock lock(lock_); - - // Unregister the dump provider if it failed too many times consecutively. - if (!mdpinfo->disabled && - mdpinfo->consecutive_failures >= kMaxConsecutiveFailuresCount) { - mdpinfo->disabled = true; - LOG(ERROR) << "Disabling MemoryDumpProvider \"" << mdpinfo->name - << "\". Dump failed multiple times consecutively."; - } - should_dump = !mdpinfo->disabled; - } // AutoLock lock(lock_); - - if (should_dump) { - // Invoke the dump provider. - TRACE_EVENT1(kTraceCategory, "MemoryDumpManager::InvokeOnMemoryDump", - "dump_provider.name", mdpinfo->name); - - // A stack allocated string with dump provider name is useful to debug - // crashes while invoking dump after a |dump_provider| is not unregistered - // in safe way. - // TODO(ssid): Remove this after fixing crbug.com/643438. - char provider_name_for_debugging[16]; - strncpy(provider_name_for_debugging, mdpinfo->name, - sizeof(provider_name_for_debugging) - 1); - provider_name_for_debugging[sizeof(provider_name_for_debugging) - 1] = '\0'; - base::debug::Alias(provider_name_for_debugging); - - // Pid of the target process being dumped. Often kNullProcessId (= current - // process), non-zero when the coordinator process creates dumps on behalf - // of child processes (see crbug.com/461788). - ProcessId target_pid = mdpinfo->options.target_pid; - MemoryDumpArgs args = {pmd_async_state->req_args.level_of_detail}; - ProcessMemoryDump* pmd = - pmd_async_state->GetOrCreateMemoryDumpContainerForProcess(target_pid, - args); - bool dump_successful = mdpinfo->dump_provider->OnMemoryDump(args, pmd); - mdpinfo->consecutive_failures = - dump_successful ? 0 : mdpinfo->consecutive_failures + 1; - } - - pmd_async_state->pending_dump_providers.pop_back(); - SetupNextMemoryDump(std::move(pmd_async_state)); -} - -bool MemoryDumpManager::PollFastMemoryTotal(uint64_t* memory_total) { -#if DCHECK_IS_ON() - { - AutoLock lock(lock_); - if (dump_thread_) - DCHECK(dump_thread_->task_runner()->BelongsToCurrentThread()); - } -#endif - if (dump_providers_for_polling_.empty()) - return false; - - *memory_total = 0; - // Note that we call PollFastMemoryTotal() even if the dump provider is - // disabled (unregistered). This is to avoid taking lock while polling. - for (const auto& mdpinfo : dump_providers_for_polling_) { - uint64_t value = 0; - mdpinfo->dump_provider->PollFastMemoryTotal(&value); - *memory_total += value; - } - return true; -} - -// static -uint32_t MemoryDumpManager::GetDumpsSumKb(const std::string& pattern, - const ProcessMemoryDump* pmd) { - uint64_t sum = 0; - for (const auto& kv : pmd->allocator_dumps()) { - auto name = StringPiece(kv.first); - if (MatchPattern(name, pattern)) - sum += kv.second->GetSize(); - } - return sum / 1024; -} - -// static -void MemoryDumpManager::FinalizeDumpAndAddToTrace( - std::unique_ptr<ProcessMemoryDumpAsyncState> pmd_async_state) { - HEAP_PROFILER_SCOPED_IGNORE; - DCHECK(pmd_async_state->pending_dump_providers.empty()); - const uint64_t dump_guid = pmd_async_state->req_args.dump_guid; - if (!pmd_async_state->callback_task_runner->BelongsToCurrentThread()) { - scoped_refptr<SingleThreadTaskRunner> callback_task_runner = - pmd_async_state->callback_task_runner; - callback_task_runner->PostTask( - FROM_HERE, Bind(&MemoryDumpManager::FinalizeDumpAndAddToTrace, - Passed(&pmd_async_state))); - return; - } - - TRACE_EVENT0(kTraceCategory, "MemoryDumpManager::FinalizeDumpAndAddToTrace"); - - // The results struct to fill. - // TODO(hjd): Transitional until we send the full PMD. See crbug.com/704203 - MemoryDumpCallbackResult result; - - for (const auto& kv : pmd_async_state->process_dumps) { - ProcessId pid = kv.first; // kNullProcessId for the current process. - ProcessMemoryDump* process_memory_dump = kv.second.get(); - std::unique_ptr<TracedValue> traced_value(new TracedValue); - process_memory_dump->AsValueInto(traced_value.get()); - traced_value->SetString("level_of_detail", - MemoryDumpLevelOfDetailToString( - pmd_async_state->req_args.level_of_detail)); - const char* const event_name = - MemoryDumpTypeToString(pmd_async_state->req_args.dump_type); - - std::unique_ptr<ConvertableToTraceFormat> event_value( - std::move(traced_value)); - TRACE_EVENT_API_ADD_TRACE_EVENT_WITH_PROCESS_ID( - TRACE_EVENT_PHASE_MEMORY_DUMP, - TraceLog::GetCategoryGroupEnabled(kTraceCategory), event_name, - trace_event_internal::kGlobalScope, dump_guid, pid, - kTraceEventNumArgs, kTraceEventArgNames, - kTraceEventArgTypes, nullptr /* arg_values */, &event_value, - TRACE_EVENT_FLAG_HAS_ID); - - // TODO(hjd): Transitional until we send the full PMD. See crbug.com/704203 - // Don't try to fill the struct in detailed mode since it is hard to avoid - // double counting. - if (pmd_async_state->req_args.level_of_detail == - MemoryDumpLevelOfDetail::DETAILED) - continue; - - // TODO(hjd): Transitional until we send the full PMD. See crbug.com/704203 - if (pid == kNullProcessId) { - result.chrome_dump.malloc_total_kb = - GetDumpsSumKb("malloc", process_memory_dump); - result.chrome_dump.v8_total_kb = - GetDumpsSumKb("v8/*", process_memory_dump); - - // partition_alloc reports sizes for both allocated_objects and - // partitions. The memory allocated_objects uses is a subset of - // the partitions memory so to avoid double counting we only - // count partitions memory. - result.chrome_dump.partition_alloc_total_kb = - GetDumpsSumKb("partition_alloc/partitions/*", process_memory_dump); - result.chrome_dump.blink_gc_total_kb = - GetDumpsSumKb("blink_gc", process_memory_dump); - } - } - - bool tracing_still_enabled; - TRACE_EVENT_CATEGORY_GROUP_ENABLED(kTraceCategory, &tracing_still_enabled); - if (!tracing_still_enabled) { - pmd_async_state->dump_successful = false; - VLOG(1) << kLogPrefix << " failed because tracing was disabled before" - << " the dump was completed"; - } - - if (!pmd_async_state->callback.is_null()) { - pmd_async_state->callback.Run(dump_guid, pmd_async_state->dump_successful); - pmd_async_state->callback.Reset(); - } - - TRACE_EVENT_NESTABLE_ASYNC_END0(kTraceCategory, "ProcessMemoryDump", - TRACE_ID_LOCAL(dump_guid)); -} - -void MemoryDumpManager::OnTraceLogEnabled() { - bool enabled; - TRACE_EVENT_CATEGORY_GROUP_ENABLED(kTraceCategory, &enabled); - if (!enabled) - return; - - // Initialize the TraceLog for the current thread. This is to avoid that the - // TraceLog memory dump provider is registered lazily in the PostTask() below - // while the |lock_| is taken; - TraceLog::GetInstance()->InitializeThreadLocalEventBufferIfSupported(); - - // Spin-up the thread used to invoke unbound dump providers. - std::unique_ptr<Thread> dump_thread(new Thread("MemoryInfra")); - if (!dump_thread->Start()) { - LOG(ERROR) << "Failed to start the memory-infra thread for tracing"; - return; - } - - const TraceConfig& trace_config = - TraceLog::GetInstance()->GetCurrentTraceConfig(); - const TraceConfig::MemoryDumpConfig& memory_dump_config = - trace_config.memory_dump_config(); - scoped_refptr<MemoryDumpSessionState> session_state = - new MemoryDumpSessionState; - session_state->SetAllowedDumpModes(memory_dump_config.allowed_dump_modes); - session_state->set_heap_profiler_breakdown_threshold_bytes( - memory_dump_config.heap_profiler_options.breakdown_threshold_bytes); - if (heap_profiling_enabled_) { - // If heap profiling is enabled, the stack frame deduplicator and type name - // deduplicator will be in use. Add a metadata events to write the frames - // and type IDs. - session_state->SetStackFrameDeduplicator( - WrapUnique(new StackFrameDeduplicator)); - - session_state->SetTypeNameDeduplicator( - WrapUnique(new TypeNameDeduplicator)); - - TRACE_EVENT_API_ADD_METADATA_EVENT( - TraceLog::GetCategoryGroupEnabled("__metadata"), "stackFrames", - "stackFrames", - MakeUnique<SessionStateConvertableProxy<StackFrameDeduplicator>>( - session_state, &MemoryDumpSessionState::stack_frame_deduplicator)); - - TRACE_EVENT_API_ADD_METADATA_EVENT( - TraceLog::GetCategoryGroupEnabled("__metadata"), "typeNames", - "typeNames", - MakeUnique<SessionStateConvertableProxy<TypeNameDeduplicator>>( - session_state, &MemoryDumpSessionState::type_name_deduplicator)); - } - - { - AutoLock lock(lock_); - - DCHECK(delegate_); // At this point we must have a delegate. - session_state_ = session_state; - - DCHECK(!dump_thread_); - dump_thread_ = std::move(dump_thread); - - subtle::NoBarrier_Store(&memory_tracing_enabled_, 1); - - dump_providers_for_polling_.clear(); - for (const auto& mdpinfo : dump_providers_) { - if (mdpinfo->options.is_fast_polling_supported) - dump_providers_for_polling_.insert(mdpinfo); - } - - MemoryDumpScheduler* dump_scheduler = MemoryDumpScheduler::GetInstance(); - dump_scheduler->Setup(this, dump_thread_->task_runner()); - DCHECK_LE(memory_dump_config.triggers.size(), 3u); - for (const auto& trigger : memory_dump_config.triggers) { - if (!session_state_->IsDumpModeAllowed(trigger.level_of_detail)) { - NOTREACHED(); - continue; - } - dump_scheduler->AddTrigger(trigger.trigger_type, trigger.level_of_detail, - trigger.min_time_between_dumps_ms); - } - - // Notify polling supported only if some polling supported provider was - // registered, else RegisterPollingMDPOnDumpThread() will notify when first - // polling MDP registers. - if (!dump_providers_for_polling_.empty()) - dump_scheduler->EnablePollingIfNeeded(); - - // Only coordinator process triggers periodic global memory dumps. - if (delegate_->IsCoordinator()) - dump_scheduler->EnablePeriodicTriggerIfNeeded(); - } - -} - -void MemoryDumpManager::OnTraceLogDisabled() { - // There might be a memory dump in progress while this happens. Therefore, - // ensure that the MDM state which depends on the tracing enabled / disabled - // state is always accessed by the dumping methods holding the |lock_|. - if (!subtle::NoBarrier_Load(&memory_tracing_enabled_)) - return; - subtle::NoBarrier_Store(&memory_tracing_enabled_, 0); - std::unique_ptr<Thread> dump_thread; - { - AutoLock lock(lock_); - dump_thread = std::move(dump_thread_); - session_state_ = nullptr; - MemoryDumpScheduler::GetInstance()->DisableAllTriggers(); - } - - // Thread stops are blocking and must be performed outside of the |lock_| - // or will deadlock (e.g., if SetupNextMemoryDump() tries to acquire it). - if (dump_thread) - dump_thread->Stop(); - - // |dump_providers_for_polling_| must be cleared only after the dump thread is - // stopped (polling tasks are done). - { - AutoLock lock(lock_); - for (const auto& mdpinfo : dump_providers_for_polling_) - mdpinfo->dump_provider->SuspendFastMemoryPolling(); - dump_providers_for_polling_.clear(); - } -} - -bool MemoryDumpManager::IsDumpModeAllowed(MemoryDumpLevelOfDetail dump_mode) { - AutoLock lock(lock_); - if (!session_state_) - return false; - return session_state_->IsDumpModeAllowed(dump_mode); -} - -MemoryDumpManager::ProcessMemoryDumpAsyncState::ProcessMemoryDumpAsyncState( - MemoryDumpRequestArgs req_args, - const MemoryDumpProviderInfo::OrderedSet& dump_providers, - scoped_refptr<MemoryDumpSessionState> session_state, - MemoryDumpCallback callback, - scoped_refptr<SingleThreadTaskRunner> dump_thread_task_runner) - : req_args(req_args), - session_state(std::move(session_state)), - callback(callback), - dump_successful(true), - callback_task_runner(ThreadTaskRunnerHandle::Get()), - dump_thread_task_runner(std::move(dump_thread_task_runner)) { - pending_dump_providers.reserve(dump_providers.size()); - pending_dump_providers.assign(dump_providers.rbegin(), dump_providers.rend()); -} - -MemoryDumpManager::ProcessMemoryDumpAsyncState::~ProcessMemoryDumpAsyncState() { -} - -ProcessMemoryDump* MemoryDumpManager::ProcessMemoryDumpAsyncState:: - GetOrCreateMemoryDumpContainerForProcess(ProcessId pid, - const MemoryDumpArgs& dump_args) { - auto iter = process_dumps.find(pid); - if (iter == process_dumps.end()) { - std::unique_ptr<ProcessMemoryDump> new_pmd( - new ProcessMemoryDump(session_state, dump_args)); - iter = process_dumps.insert(std::make_pair(pid, std::move(new_pmd))).first; - } - return iter->second.get(); -} - -} // namespace trace_event -} // namespace base diff --git a/base/trace_event/memory_dump_manager.h b/base/trace_event/memory_dump_manager.h deleted file mode 100644 index e7f5194850..0000000000 --- a/base/trace_event/memory_dump_manager.h +++ /dev/null @@ -1,354 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_TRACE_EVENT_MEMORY_DUMP_MANAGER_H_ -#define BASE_TRACE_EVENT_MEMORY_DUMP_MANAGER_H_ - -#include <stdint.h> - -#include <map> -#include <memory> -#include <unordered_set> -#include <vector> - -#include "base/atomicops.h" -#include "base/containers/hash_tables.h" -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "base/memory/singleton.h" -#include "base/synchronization/lock.h" -#include "base/trace_event/memory_allocator_dump.h" -#include "base/trace_event/memory_dump_provider_info.h" -#include "base/trace_event/memory_dump_request_args.h" -#include "base/trace_event/process_memory_dump.h" -#include "base/trace_event/trace_event.h" - -// Forward declare |MemoryDumpManagerDelegateImplTest| so that we can make it a -// friend of |MemoryDumpManager| and give it access to |SetInstanceForTesting|. -namespace memory_instrumentation { - -class MemoryDumpManagerDelegateImplTest; - -} // namespace memory_instrumentation - -namespace base { - -class SingleThreadTaskRunner; -class Thread; - -namespace trace_event { - -class MemoryDumpManagerDelegate; -class MemoryDumpProvider; -class MemoryDumpSessionState; -class MemoryDumpScheduler; - -// This is the interface exposed to the rest of the codebase to deal with -// memory tracing. The main entry point for clients is represented by -// RequestDumpPoint(). The extension by Un(RegisterDumpProvider). -class BASE_EXPORT MemoryDumpManager : public TraceLog::EnabledStateObserver { - public: - static const char* const kTraceCategory; - static const char* const kLogPrefix; - - // This value is returned as the tracing id of the child processes by - // GetTracingProcessId() when tracing is not enabled. - static const uint64_t kInvalidTracingProcessId; - - static MemoryDumpManager* GetInstance(); - - // Invoked once per process to listen to trace begin / end events. - // Initialization can happen after (Un)RegisterMemoryDumpProvider() calls - // and the MemoryDumpManager guarantees to support this. - // On the other side, the MemoryDumpManager will not be fully operational - // (i.e. will NACK any RequestGlobalMemoryDump()) until initialized. - // Arguments: - // delegate: inversion-of-control interface for embedder-specific behaviors - // (multiprocess handshaking). See the lifetime and thread-safety - // requirements in the |MemoryDumpManagerDelegate| docstring. - void Initialize(std::unique_ptr<MemoryDumpManagerDelegate> delegate); - - // (Un)Registers a MemoryDumpProvider instance. - // Args: - // - mdp: the MemoryDumpProvider instance to be registered. MemoryDumpManager - // does NOT take memory ownership of |mdp|, which is expected to either - // be a singleton or unregister itself. - // - name: a friendly name (duplicates allowed). Used for debugging and - // run-time profiling of memory-infra internals. Must be a long-lived - // C string. - // - task_runner: either a SingleThreadTaskRunner or SequencedTaskRunner. All - // the calls to |mdp| will be run on the given |task_runner|. If passed - // null |mdp| should be able to handle calls on arbitrary threads. - // - options: extra optional arguments. See memory_dump_provider.h. - void RegisterDumpProvider(MemoryDumpProvider* mdp, - const char* name, - scoped_refptr<SingleThreadTaskRunner> task_runner); - void RegisterDumpProvider(MemoryDumpProvider* mdp, - const char* name, - scoped_refptr<SingleThreadTaskRunner> task_runner, - MemoryDumpProvider::Options options); - void RegisterDumpProviderWithSequencedTaskRunner( - MemoryDumpProvider* mdp, - const char* name, - scoped_refptr<SequencedTaskRunner> task_runner, - MemoryDumpProvider::Options options); - void UnregisterDumpProvider(MemoryDumpProvider* mdp); - - // Unregisters an unbound dump provider and takes care about its deletion - // asynchronously. Can be used only for for dump providers with no - // task-runner affinity. - // This method takes ownership of the dump provider and guarantees that: - // - The |mdp| will be deleted at some point in the near future. - // - Its deletion will not happen concurrently with the OnMemoryDump() call. - // Note that OnMemoryDump() and PollFastMemoryTotal() calls can still happen - // after this method returns. - void UnregisterAndDeleteDumpProviderSoon( - std::unique_ptr<MemoryDumpProvider> mdp); - - // Requests a memory dump. The dump might happen or not depending on the - // filters and categories specified when enabling tracing. - // The optional |callback| is executed asynchronously, on an arbitrary thread, - // to notify about the completion of the global dump (i.e. after all the - // processes have dumped) and its success (true iff all the dumps were - // successful). - void RequestGlobalDump(MemoryDumpType dump_type, - MemoryDumpLevelOfDetail level_of_detail, - const MemoryDumpCallback& callback); - - // Same as above (still asynchronous), but without callback. - void RequestGlobalDump(MemoryDumpType dump_type, - MemoryDumpLevelOfDetail level_of_detail); - - // TraceLog::EnabledStateObserver implementation. - void OnTraceLogEnabled() override; - void OnTraceLogDisabled() override; - - // Enable heap profiling if kEnableHeapProfiling is specified. - void EnableHeapProfilingIfNeeded(); - - // Returns true if the dump mode is allowed for current tracing session. - bool IsDumpModeAllowed(MemoryDumpLevelOfDetail dump_mode); - - // Lets tests see if a dump provider is registered. - bool IsDumpProviderRegisteredForTesting(MemoryDumpProvider*); - - // Returns the MemoryDumpSessionState object, which is shared by all the - // ProcessMemoryDump and MemoryAllocatorDump instances through all the tracing - // session lifetime. - const scoped_refptr<MemoryDumpSessionState>& session_state_for_testing() - const { - return session_state_; - } - - // Returns a unique id for identifying the processes. The id can be - // retrieved by child processes only when tracing is enabled. This is - // intended to express cross-process sharing of memory dumps on the - // child-process side, without having to know its own child process id. - uint64_t GetTracingProcessId() const { return tracing_process_id_; } - void set_tracing_process_id(uint64_t tracing_process_id) { - tracing_process_id_ = tracing_process_id; - } - - // Returns the name for a the allocated_objects dump. Use this to declare - // suballocator dumps from other dump providers. - // It will return nullptr if there is no dump provider for the system - // allocator registered (which is currently the case for Mac OS). - const char* system_allocator_pool_name() const { - return kSystemAllocatorPoolName; - }; - - // When set to true, calling |RegisterMemoryDumpProvider| is a no-op. - void set_dumper_registrations_ignored_for_testing(bool ignored) { - dumper_registrations_ignored_for_testing_ = ignored; - } - - private: - friend std::default_delete<MemoryDumpManager>; // For the testing instance. - friend struct DefaultSingletonTraits<MemoryDumpManager>; - friend class MemoryDumpManagerDelegate; - friend class MemoryDumpManagerTest; - friend class MemoryDumpScheduler; - friend class memory_instrumentation::MemoryDumpManagerDelegateImplTest; - - // Holds the state of a process memory dump that needs to be carried over - // across task runners in order to fulfil an asynchronous CreateProcessDump() - // request. At any time exactly one task runner owns a - // ProcessMemoryDumpAsyncState. - struct ProcessMemoryDumpAsyncState { - ProcessMemoryDumpAsyncState( - MemoryDumpRequestArgs req_args, - const MemoryDumpProviderInfo::OrderedSet& dump_providers, - scoped_refptr<MemoryDumpSessionState> session_state, - MemoryDumpCallback callback, - scoped_refptr<SingleThreadTaskRunner> dump_thread_task_runner); - ~ProcessMemoryDumpAsyncState(); - - // Gets or creates the memory dump container for the given target process. - ProcessMemoryDump* GetOrCreateMemoryDumpContainerForProcess( - ProcessId pid, - const MemoryDumpArgs& dump_args); - - // A map of ProcessId -> ProcessMemoryDump, one for each target process - // being dumped from the current process. Typically each process dumps only - // for itself, unless dump providers specify a different |target_process| in - // MemoryDumpProvider::Options. - std::map<ProcessId, std::unique_ptr<ProcessMemoryDump>> process_dumps; - - // The arguments passed to the initial CreateProcessDump() request. - const MemoryDumpRequestArgs req_args; - - // An ordered sequence of dump providers that have to be invoked to complete - // the dump. This is a copy of |dump_providers_| at the beginning of a dump - // and becomes empty at the end, when all dump providers have been invoked. - std::vector<scoped_refptr<MemoryDumpProviderInfo>> pending_dump_providers; - - // The trace-global session state. - scoped_refptr<MemoryDumpSessionState> session_state; - - // Callback passed to the initial call to CreateProcessDump(). - MemoryDumpCallback callback; - - // The |success| field that will be passed as argument to the |callback|. - bool dump_successful; - - // The thread on which FinalizeDumpAndAddToTrace() (and hence |callback|) - // should be invoked. This is the thread on which the initial - // CreateProcessDump() request was called. - const scoped_refptr<SingleThreadTaskRunner> callback_task_runner; - - // The thread on which unbound dump providers should be invoked. - // This is essentially |dump_thread_|.task_runner() but needs to be kept - // as a separate variable as it needs to be accessed by arbitrary dumpers' - // threads outside of the lock_ to avoid races when disabling tracing. - // It is immutable for all the duration of a tracing session. - const scoped_refptr<SingleThreadTaskRunner> dump_thread_task_runner; - - private: - DISALLOW_COPY_AND_ASSIGN(ProcessMemoryDumpAsyncState); - }; - - static const int kMaxConsecutiveFailuresCount; - static const char* const kSystemAllocatorPoolName; - - MemoryDumpManager(); - ~MemoryDumpManager() override; - - static void SetInstanceForTesting(MemoryDumpManager* instance); - static uint32_t GetDumpsSumKb(const std::string&, const ProcessMemoryDump*); - static void FinalizeDumpAndAddToTrace( - std::unique_ptr<ProcessMemoryDumpAsyncState> pmd_async_state); - - // Internal, used only by MemoryDumpManagerDelegate. - // Creates a memory dump for the current process and appends it to the trace. - // |callback| will be invoked asynchronously upon completion on the same - // thread on which CreateProcessDump() was called. - void CreateProcessDump(const MemoryDumpRequestArgs& args, - const MemoryDumpCallback& callback); - - // Calls InvokeOnMemoryDump() for the next MDP on the task runner specified by - // the MDP while registration. On failure to do so, skips and continues to - // next MDP. - void SetupNextMemoryDump( - std::unique_ptr<ProcessMemoryDumpAsyncState> pmd_async_state); - - // Invokes OnMemoryDump() of the next MDP and calls SetupNextMemoryDump() at - // the end to continue the ProcessMemoryDump. Should be called on the MDP task - // runner. - void InvokeOnMemoryDump(ProcessMemoryDumpAsyncState* owned_pmd_async_state); - - // Records a quick total memory usage in |memory_total|. This is used to track - // and detect peaks in the memory usage of the process without having to - // record all data from dump providers. This value is approximate to trade-off - // speed, and not consistent with the rest of the memory-infra metrics. Must - // be called on the dump thread. - // Returns true if |memory_total| was updated by polling at least 1 MDP. - bool PollFastMemoryTotal(uint64_t* memory_total); - - // Helper for RegierDumpProvider* functions. - void RegisterDumpProviderInternal( - MemoryDumpProvider* mdp, - const char* name, - scoped_refptr<SequencedTaskRunner> task_runner, - const MemoryDumpProvider::Options& options); - - // Helper for the public UnregisterDumpProvider* functions. - void UnregisterDumpProviderInternal(MemoryDumpProvider* mdp, - bool take_mdp_ownership_and_delete_async); - - // Adds / removes provider that supports polling to - // |dump_providers_for_polling_|. - void RegisterPollingMDPOnDumpThread( - scoped_refptr<MemoryDumpProviderInfo> mdpinfo); - void UnregisterPollingMDPOnDumpThread( - scoped_refptr<MemoryDumpProviderInfo> mdpinfo); - - // An ordererd set of registered MemoryDumpProviderInfo(s), sorted by task - // runner affinity (MDPs belonging to the same task runners are adjacent). - MemoryDumpProviderInfo::OrderedSet dump_providers_; - - // A copy of mdpinfo list that support polling. It must be accessed only on - // the dump thread if dump thread exists. - MemoryDumpProviderInfo::OrderedSet dump_providers_for_polling_; - - // Shared among all the PMDs to keep state scoped to the tracing session. - scoped_refptr<MemoryDumpSessionState> session_state_; - - // The list of names of dump providers that are blacklisted from strict thread - // affinity check on unregistration. - std::unordered_set<StringPiece, StringPieceHash> - strict_thread_check_blacklist_; - - std::unique_ptr<MemoryDumpManagerDelegate> delegate_; - - // Protects from concurrent accesses to the |dump_providers_*| and |delegate_| - // to guard against disabling logging while dumping on another thread. - Lock lock_; - - // Optimization to avoid attempting any memory dump (i.e. to not walk an empty - // dump_providers_enabled_ list) when tracing is not enabled. - subtle::AtomicWord memory_tracing_enabled_; - - // Thread used for MemoryDumpProviders which don't specify a task runner - // affinity. - std::unique_ptr<Thread> dump_thread_; - - // The unique id of the child process. This is created only for tracing and is - // expected to be valid only when tracing is enabled. - uint64_t tracing_process_id_; - - // When true, calling |RegisterMemoryDumpProvider| is a no-op. - bool dumper_registrations_ignored_for_testing_; - - // Whether new memory dump providers should be told to enable heap profiling. - bool heap_profiling_enabled_; - - DISALLOW_COPY_AND_ASSIGN(MemoryDumpManager); -}; - -// The delegate is supposed to be long lived (read: a Singleton) and thread -// safe (i.e. should expect calls from any thread and handle thread hopping). -class BASE_EXPORT MemoryDumpManagerDelegate { - public: - MemoryDumpManagerDelegate() {} - virtual ~MemoryDumpManagerDelegate() {} - - virtual void RequestGlobalMemoryDump(const MemoryDumpRequestArgs& args, - const MemoryDumpCallback& callback) = 0; - - virtual bool IsCoordinator() const = 0; - - protected: - void CreateProcessDump(const MemoryDumpRequestArgs& args, - const MemoryDumpCallback& callback) { - MemoryDumpManager::GetInstance()->CreateProcessDump(args, callback); - } - - private: - DISALLOW_COPY_AND_ASSIGN(MemoryDumpManagerDelegate); -}; - -} // namespace trace_event -} // namespace base - -#endif // BASE_TRACE_EVENT_MEMORY_DUMP_MANAGER_H_ diff --git a/base/trace_event/memory_dump_manager_unittest.cc b/base/trace_event/memory_dump_manager_unittest.cc deleted file mode 100644 index e126edd397..0000000000 --- a/base/trace_event/memory_dump_manager_unittest.cc +++ /dev/null @@ -1,1311 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/trace_event/memory_dump_manager.h" - -#include <stdint.h> - -#include <memory> -#include <utility> -#include <vector> - -#include "base/bind_helpers.h" -#include "base/callback.h" -#include "base/memory/ptr_util.h" -#include "base/memory/ref_counted_memory.h" -#include "base/message_loop/message_loop.h" -#include "base/run_loop.h" -#include "base/strings/stringprintf.h" -#include "base/synchronization/waitable_event.h" -#include "base/test/sequenced_worker_pool_owner.h" -#include "base/test/test_io_thread.h" -#include "base/test/trace_event_analyzer.h" -#include "base/threading/platform_thread.h" -#include "base/threading/sequenced_task_runner_handle.h" -#include "base/threading/sequenced_worker_pool.h" -#include "base/threading/thread.h" -#include "base/threading/thread_task_runner_handle.h" -#include "base/trace_event/memory_dump_provider.h" -#include "base/trace_event/memory_dump_scheduler.h" -#include "base/trace_event/memory_infra_background_whitelist.h" -#include "base/trace_event/process_memory_dump.h" -#include "base/trace_event/trace_buffer.h" -#include "base/trace_event/trace_config_memory_test_util.h" -#include "build/build_config.h" -#include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" - -using testing::_; -using testing::AnyNumber; -using testing::AtMost; -using testing::Between; -using testing::Invoke; -using testing::Return; - -namespace base { -namespace trace_event { - -// GTest matchers for MemoryDumpRequestArgs arguments. -MATCHER(IsDetailedDump, "") { - return arg.level_of_detail == MemoryDumpLevelOfDetail::DETAILED; -} - -MATCHER(IsLightDump, "") { - return arg.level_of_detail == MemoryDumpLevelOfDetail::LIGHT; -} - -MATCHER(IsBackgroundDump, "") { - return arg.level_of_detail == MemoryDumpLevelOfDetail::BACKGROUND; -} - -namespace { - -const char* kMDPName = "TestDumpProvider"; -const char* kWhitelistedMDPName = "WhitelistedTestDumpProvider"; -const char* const kTestMDPWhitelist[] = {kWhitelistedMDPName, nullptr}; - -void RegisterDumpProvider( - MemoryDumpProvider* mdp, - scoped_refptr<base::SingleThreadTaskRunner> task_runner, - const MemoryDumpProvider::Options& options, - const char* name = kMDPName) { - MemoryDumpManager* mdm = MemoryDumpManager::GetInstance(); - mdm->set_dumper_registrations_ignored_for_testing(false); - mdm->RegisterDumpProvider(mdp, name, std::move(task_runner), options); - mdm->set_dumper_registrations_ignored_for_testing(true); -} - -void RegisterDumpProvider( - MemoryDumpProvider* mdp, - scoped_refptr<base::SingleThreadTaskRunner> task_runner) { - RegisterDumpProvider(mdp, task_runner, MemoryDumpProvider::Options()); -} - -void RegisterDumpProviderWithSequencedTaskRunner( - MemoryDumpProvider* mdp, - scoped_refptr<base::SequencedTaskRunner> task_runner, - const MemoryDumpProvider::Options& options) { - MemoryDumpManager* mdm = MemoryDumpManager::GetInstance(); - mdm->set_dumper_registrations_ignored_for_testing(false); - mdm->RegisterDumpProviderWithSequencedTaskRunner(mdp, kMDPName, task_runner, - options); - mdm->set_dumper_registrations_ignored_for_testing(true); -} - -void OnTraceDataCollected(Closure quit_closure, - trace_event::TraceResultBuffer* buffer, - const scoped_refptr<RefCountedString>& json, - bool has_more_events) { - buffer->AddFragment(json->data()); - if (!has_more_events) - quit_closure.Run(); -} - -// Posts |task| to |task_runner| and blocks until it is executed. -void PostTaskAndWait(const tracked_objects::Location& from_here, - SequencedTaskRunner* task_runner, - base::OnceClosure task) { - base::WaitableEvent event(WaitableEvent::ResetPolicy::MANUAL, - WaitableEvent::InitialState::NOT_SIGNALED); - task_runner->PostTask(from_here, std::move(task)); - task_runner->PostTask( - FROM_HERE, base::Bind(&WaitableEvent::Signal, base::Unretained(&event))); - // The SequencedTaskRunner guarantees that |event| will only be signaled after - // |task| is executed. - event.Wait(); -} - -// Testing MemoryDumpManagerDelegate which, by default, short-circuits dump -// requests locally to the MemoryDumpManager instead of performing IPC dances. -class MemoryDumpManagerDelegateForTesting : public MemoryDumpManagerDelegate { - public: - MemoryDumpManagerDelegateForTesting(bool is_coordinator) - : is_coordinator_(is_coordinator) { - ON_CALL(*this, RequestGlobalMemoryDump(_, _)) - .WillByDefault(Invoke( - this, &MemoryDumpManagerDelegateForTesting::CreateProcessDump)); - } - - MOCK_METHOD2(RequestGlobalMemoryDump, - void(const MemoryDumpRequestArgs& args, - const MemoryDumpCallback& callback)); - - bool IsCoordinator() const override { return is_coordinator_; } - - // Promote the CreateProcessDump to public so it can be used by test fixtures. - using MemoryDumpManagerDelegate::CreateProcessDump; - - private: - bool is_coordinator_; -}; - -class MockMemoryDumpProvider : public MemoryDumpProvider { - public: - MOCK_METHOD0(Destructor, void()); - MOCK_METHOD2(OnMemoryDump, - bool(const MemoryDumpArgs& args, ProcessMemoryDump* pmd)); - MOCK_METHOD1(PollFastMemoryTotal, void(uint64_t* memory_total)); - MOCK_METHOD0(SuspendFastMemoryPolling, void()); - - MockMemoryDumpProvider() : enable_mock_destructor(false) { - ON_CALL(*this, OnMemoryDump(_, _)) - .WillByDefault(Invoke([](const MemoryDumpArgs&, - ProcessMemoryDump* pmd) -> bool { - // |session_state| should not be null under any circumstances when - // invoking a memory dump. The problem might arise in race conditions - // like crbug.com/600570 . - EXPECT_TRUE(pmd->session_state().get() != nullptr); - return true; - })); - - ON_CALL(*this, PollFastMemoryTotal(_)) - .WillByDefault( - Invoke([](uint64_t* memory_total) -> void { NOTREACHED(); })); - } - ~MockMemoryDumpProvider() override { - if (enable_mock_destructor) - Destructor(); - } - - bool enable_mock_destructor; -}; - -class TestSequencedTaskRunner : public SequencedTaskRunner { - public: - TestSequencedTaskRunner() - : worker_pool_(2 /* max_threads */, "Test Task Runner"), - enabled_(true), - num_of_post_tasks_(0) {} - - void set_enabled(bool value) { enabled_ = value; } - unsigned no_of_post_tasks() const { return num_of_post_tasks_; } - - bool PostNonNestableDelayedTask(const tracked_objects::Location& from_here, - OnceClosure task, - TimeDelta delay) override { - NOTREACHED(); - return false; - } - - bool PostDelayedTask(const tracked_objects::Location& from_here, - OnceClosure task, - TimeDelta delay) override { - num_of_post_tasks_++; - if (enabled_) { - return worker_pool_.pool()->PostSequencedWorkerTask(token_, from_here, - std::move(task)); - } - return false; - } - - bool RunsTasksOnCurrentThread() const override { - return worker_pool_.pool()->RunsTasksOnCurrentThread(); - } - - private: - ~TestSequencedTaskRunner() override {} - - SequencedWorkerPoolOwner worker_pool_; - const SequencedWorkerPool::SequenceToken token_; - bool enabled_; - unsigned num_of_post_tasks_; -}; - -} // namespace - -class MemoryDumpManagerTest : public testing::Test { - public: - MemoryDumpManagerTest() : testing::Test(), kDefaultOptions() {} - - void SetUp() override { - last_callback_success_ = false; - message_loop_.reset(new MessageLoop()); - mdm_.reset(new MemoryDumpManager()); - MemoryDumpManager::SetInstanceForTesting(mdm_.get()); - ASSERT_EQ(mdm_.get(), MemoryDumpManager::GetInstance()); - } - - void TearDown() override { - MemoryDumpManager::SetInstanceForTesting(nullptr); - delegate_ = nullptr; - mdm_.reset(); - message_loop_.reset(); - TraceLog::DeleteForTesting(); - } - - // Turns a Closure into a MemoryDumpCallback, keeping track of the callback - // result and taking care of posting the closure on the correct task runner. - void DumpCallbackAdapter(scoped_refptr<SingleThreadTaskRunner> task_runner, - Closure closure, - uint64_t dump_guid, - bool success) { - last_callback_success_ = success; - task_runner->PostTask(FROM_HERE, closure); - } - - void PollFastMemoryTotal(uint64_t* memory_total) { - mdm_->PollFastMemoryTotal(memory_total); - } - - protected: - void InitializeMemoryDumpManager(bool is_coordinator) { - mdm_->set_dumper_registrations_ignored_for_testing(true); - delegate_ = new MemoryDumpManagerDelegateForTesting(is_coordinator); - mdm_->Initialize(base::WrapUnique(delegate_)); - } - - void RequestGlobalDumpAndWait(MemoryDumpType dump_type, - MemoryDumpLevelOfDetail level_of_detail) { - RunLoop run_loop; - MemoryDumpCallback callback = - Bind(&MemoryDumpManagerTest::DumpCallbackAdapter, Unretained(this), - ThreadTaskRunnerHandle::Get(), run_loop.QuitClosure()); - mdm_->RequestGlobalDump(dump_type, level_of_detail, callback); - run_loop.Run(); - } - - void EnableTracingWithLegacyCategories(const char* category) { - TraceLog::GetInstance()->SetEnabled(TraceConfig(category, ""), - TraceLog::RECORDING_MODE); - } - - void EnableTracingWithTraceConfig(const std::string& trace_config) { - TraceLog::GetInstance()->SetEnabled(TraceConfig(trace_config), - TraceLog::RECORDING_MODE); - } - - void DisableTracing() { TraceLog::GetInstance()->SetDisabled(); } - - bool IsPeriodicDumpingEnabled() const { - return MemoryDumpScheduler::GetInstance() - ->IsPeriodicTimerRunningForTesting(); - } - - int GetMaxConsecutiveFailuresCount() const { - return MemoryDumpManager::kMaxConsecutiveFailuresCount; - } - - const MemoryDumpProvider::Options kDefaultOptions; - std::unique_ptr<MemoryDumpManager> mdm_; - MemoryDumpManagerDelegateForTesting* delegate_; - bool last_callback_success_; - - private: - std::unique_ptr<MessageLoop> message_loop_; - - // We want our singleton torn down after each test. - ShadowingAtExitManager at_exit_manager_; -}; - -// Basic sanity checks. Registers a memory dump provider and checks that it is -// called, but only when memory-infra is enabled. -TEST_F(MemoryDumpManagerTest, SingleDumper) { - InitializeMemoryDumpManager(false /* is_coordinator */); - MockMemoryDumpProvider mdp; - RegisterDumpProvider(&mdp, ThreadTaskRunnerHandle::Get()); - - // Check that the dumper is not called if the memory category is not enabled. - EnableTracingWithLegacyCategories("foobar-but-not-memory"); - EXPECT_CALL(*delegate_, RequestGlobalMemoryDump(_, _)).Times(0); - EXPECT_CALL(mdp, OnMemoryDump(_, _)).Times(0); - RequestGlobalDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED, - MemoryDumpLevelOfDetail::DETAILED); - DisableTracing(); - - // Now repeat enabling the memory category and check that the dumper is - // invoked this time. - EnableTracingWithLegacyCategories(MemoryDumpManager::kTraceCategory); - EXPECT_CALL(*delegate_, RequestGlobalMemoryDump(_, _)).Times(3); - EXPECT_CALL(mdp, OnMemoryDump(_, _)).Times(3).WillRepeatedly(Return(true)); - for (int i = 0; i < 3; ++i) - RequestGlobalDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED, - MemoryDumpLevelOfDetail::DETAILED); - DisableTracing(); - - mdm_->UnregisterDumpProvider(&mdp); - - // Finally check the unregister logic: the delegate will be invoked but not - // the dump provider, as it has been unregistered. - EnableTracingWithLegacyCategories(MemoryDumpManager::kTraceCategory); - EXPECT_CALL(*delegate_, RequestGlobalMemoryDump(_, _)).Times(3); - EXPECT_CALL(mdp, OnMemoryDump(_, _)).Times(0); - - for (int i = 0; i < 3; ++i) { - RequestGlobalDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED, - MemoryDumpLevelOfDetail::DETAILED); - } - DisableTracing(); -} - -// Checks that requesting dumps with high level of detail actually propagates -// the level of the detail properly to OnMemoryDump() call on dump providers. -TEST_F(MemoryDumpManagerTest, CheckMemoryDumpArgs) { - InitializeMemoryDumpManager(false /* is_coordinator */); - MockMemoryDumpProvider mdp; - - RegisterDumpProvider(&mdp, ThreadTaskRunnerHandle::Get()); - EnableTracingWithLegacyCategories(MemoryDumpManager::kTraceCategory); - EXPECT_CALL(*delegate_, RequestGlobalMemoryDump(_, _)).Times(1); - EXPECT_CALL(mdp, OnMemoryDump(IsDetailedDump(), _)).WillOnce(Return(true)); - RequestGlobalDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED, - MemoryDumpLevelOfDetail::DETAILED); - DisableTracing(); - mdm_->UnregisterDumpProvider(&mdp); - - // Check that requesting dumps with low level of detail actually propagates to - // OnMemoryDump() call on dump providers. - RegisterDumpProvider(&mdp, ThreadTaskRunnerHandle::Get()); - EnableTracingWithLegacyCategories(MemoryDumpManager::kTraceCategory); - EXPECT_CALL(*delegate_, RequestGlobalMemoryDump(_, _)).Times(1); - EXPECT_CALL(mdp, OnMemoryDump(IsLightDump(), _)).WillOnce(Return(true)); - RequestGlobalDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED, - MemoryDumpLevelOfDetail::LIGHT); - DisableTracing(); - mdm_->UnregisterDumpProvider(&mdp); -} - -// Checks that the SharedSessionState object is acqually shared over time. -TEST_F(MemoryDumpManagerTest, SharedSessionState) { - InitializeMemoryDumpManager(false /* is_coordinator */); - MockMemoryDumpProvider mdp1; - MockMemoryDumpProvider mdp2; - RegisterDumpProvider(&mdp1, nullptr); - RegisterDumpProvider(&mdp2, nullptr); - - EnableTracingWithLegacyCategories(MemoryDumpManager::kTraceCategory); - const MemoryDumpSessionState* session_state = - mdm_->session_state_for_testing().get(); - EXPECT_CALL(*delegate_, RequestGlobalMemoryDump(_, _)).Times(2); - EXPECT_CALL(mdp1, OnMemoryDump(_, _)) - .Times(2) - .WillRepeatedly(Invoke([session_state](const MemoryDumpArgs&, - ProcessMemoryDump* pmd) -> bool { - EXPECT_EQ(session_state, pmd->session_state().get()); - return true; - })); - EXPECT_CALL(mdp2, OnMemoryDump(_, _)) - .Times(2) - .WillRepeatedly(Invoke([session_state](const MemoryDumpArgs&, - ProcessMemoryDump* pmd) -> bool { - EXPECT_EQ(session_state, pmd->session_state().get()); - return true; - })); - - for (int i = 0; i < 2; ++i) { - RequestGlobalDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED, - MemoryDumpLevelOfDetail::DETAILED); - } - - DisableTracing(); -} - -// Checks that the (Un)RegisterDumpProvider logic behaves sanely. -TEST_F(MemoryDumpManagerTest, MultipleDumpers) { - InitializeMemoryDumpManager(false /* is_coordinator */); - MockMemoryDumpProvider mdp1; - MockMemoryDumpProvider mdp2; - - // Enable only mdp1. - RegisterDumpProvider(&mdp1, ThreadTaskRunnerHandle::Get()); - EnableTracingWithLegacyCategories(MemoryDumpManager::kTraceCategory); - EXPECT_CALL(*delegate_, RequestGlobalMemoryDump(_, _)).Times(1); - EXPECT_CALL(mdp1, OnMemoryDump(_, _)).WillOnce(Return(true)); - EXPECT_CALL(mdp2, OnMemoryDump(_, _)).Times(0); - RequestGlobalDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED, - MemoryDumpLevelOfDetail::DETAILED); - DisableTracing(); - - // Invert: enable mdp1 and disable mdp2. - mdm_->UnregisterDumpProvider(&mdp1); - RegisterDumpProvider(&mdp2, nullptr); - EnableTracingWithLegacyCategories(MemoryDumpManager::kTraceCategory); - EXPECT_CALL(*delegate_, RequestGlobalMemoryDump(_, _)).Times(1); - EXPECT_CALL(mdp1, OnMemoryDump(_, _)).Times(0); - EXPECT_CALL(mdp2, OnMemoryDump(_, _)).WillOnce(Return(true)); - RequestGlobalDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED, - MemoryDumpLevelOfDetail::DETAILED); - DisableTracing(); - - // Enable both mdp1 and mdp2. - RegisterDumpProvider(&mdp1, nullptr); - EnableTracingWithLegacyCategories(MemoryDumpManager::kTraceCategory); - EXPECT_CALL(*delegate_, RequestGlobalMemoryDump(_, _)).Times(1); - EXPECT_CALL(mdp1, OnMemoryDump(_, _)).WillOnce(Return(true)); - EXPECT_CALL(mdp2, OnMemoryDump(_, _)).WillOnce(Return(true)); - RequestGlobalDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED, - MemoryDumpLevelOfDetail::DETAILED); - DisableTracing(); -} - -// Checks that the dump provider invocations depend only on the current -// registration state and not on previous registrations and dumps. -// Flaky on iOS, see crbug.com/706874 -#if defined(OS_IOS) -#define MAYBE_RegistrationConsistency DISABLED_RegistrationConsistency -#else -#define MAYBE_RegistrationConsistency RegistrationConsistency -#endif -TEST_F(MemoryDumpManagerTest, MAYBE_RegistrationConsistency) { - InitializeMemoryDumpManager(false /* is_coordinator */); - MockMemoryDumpProvider mdp; - - RegisterDumpProvider(&mdp, ThreadTaskRunnerHandle::Get()); - - { - EXPECT_CALL(*delegate_, RequestGlobalMemoryDump(_, _)).Times(1); - EXPECT_CALL(mdp, OnMemoryDump(_, _)).WillOnce(Return(true)); - EnableTracingWithLegacyCategories(MemoryDumpManager::kTraceCategory); - RequestGlobalDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED, - MemoryDumpLevelOfDetail::DETAILED); - DisableTracing(); - } - - mdm_->UnregisterDumpProvider(&mdp); - - { - EXPECT_CALL(*delegate_, RequestGlobalMemoryDump(_, _)).Times(1); - EXPECT_CALL(mdp, OnMemoryDump(_, _)).Times(0); - EnableTracingWithLegacyCategories(MemoryDumpManager::kTraceCategory); - RequestGlobalDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED, - MemoryDumpLevelOfDetail::DETAILED); - DisableTracing(); - } - - RegisterDumpProvider(&mdp, ThreadTaskRunnerHandle::Get()); - mdm_->UnregisterDumpProvider(&mdp); - - { - EXPECT_CALL(*delegate_, RequestGlobalMemoryDump(_, _)).Times(1); - EXPECT_CALL(mdp, OnMemoryDump(_, _)).Times(0); - EnableTracingWithLegacyCategories(MemoryDumpManager::kTraceCategory); - RequestGlobalDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED, - MemoryDumpLevelOfDetail::DETAILED); - DisableTracing(); - } - - RegisterDumpProvider(&mdp, ThreadTaskRunnerHandle::Get()); - mdm_->UnregisterDumpProvider(&mdp); - RegisterDumpProvider(&mdp, ThreadTaskRunnerHandle::Get()); - - { - EXPECT_CALL(*delegate_, RequestGlobalMemoryDump(_, _)).Times(1); - EXPECT_CALL(mdp, OnMemoryDump(_, _)).WillOnce(Return(true)); - EnableTracingWithLegacyCategories(MemoryDumpManager::kTraceCategory); - RequestGlobalDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED, - MemoryDumpLevelOfDetail::DETAILED); - DisableTracing(); - } -} - -// Checks that the MemoryDumpManager respects the thread affinity when a -// MemoryDumpProvider specifies a task_runner(). The test starts creating 8 -// threads and registering a MemoryDumpProvider on each of them. At each -// iteration, one thread is removed, to check the live unregistration logic. -TEST_F(MemoryDumpManagerTest, RespectTaskRunnerAffinity) { - InitializeMemoryDumpManager(false /* is_coordinator */); - const uint32_t kNumInitialThreads = 8; - - std::vector<std::unique_ptr<Thread>> threads; - std::vector<std::unique_ptr<MockMemoryDumpProvider>> mdps; - - // Create the threads and setup the expectations. Given that at each iteration - // we will pop out one thread/MemoryDumpProvider, each MDP is supposed to be - // invoked a number of times equal to its index. - for (uint32_t i = kNumInitialThreads; i > 0; --i) { - threads.push_back(WrapUnique(new Thread("test thread"))); - auto* thread = threads.back().get(); - thread->Start(); - scoped_refptr<SingleThreadTaskRunner> task_runner = thread->task_runner(); - mdps.push_back(WrapUnique(new MockMemoryDumpProvider())); - auto* mdp = mdps.back().get(); - RegisterDumpProvider(mdp, task_runner, kDefaultOptions); - EXPECT_CALL(*mdp, OnMemoryDump(_, _)) - .Times(i) - .WillRepeatedly(Invoke( - [task_runner](const MemoryDumpArgs&, ProcessMemoryDump*) -> bool { - EXPECT_TRUE(task_runner->RunsTasksOnCurrentThread()); - return true; - })); - } - EnableTracingWithLegacyCategories(MemoryDumpManager::kTraceCategory); - - while (!threads.empty()) { - last_callback_success_ = false; - EXPECT_CALL(*delegate_, RequestGlobalMemoryDump(_, _)).Times(1); - RequestGlobalDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED, - MemoryDumpLevelOfDetail::DETAILED); - EXPECT_TRUE(last_callback_success_); - - // Unregister a MDP and destroy one thread at each iteration to check the - // live unregistration logic. The unregistration needs to happen on the same - // thread the MDP belongs to. - { - RunLoop run_loop; - Closure unregistration = - Bind(&MemoryDumpManager::UnregisterDumpProvider, - Unretained(mdm_.get()), Unretained(mdps.back().get())); - threads.back()->task_runner()->PostTaskAndReply(FROM_HERE, unregistration, - run_loop.QuitClosure()); - run_loop.Run(); - } - mdps.pop_back(); - threads.back()->Stop(); - threads.pop_back(); - } - - DisableTracing(); -} - -// Check that the memory dump calls are always posted on task runner for -// SequencedTaskRunner case and that the dump provider gets disabled when -// PostTask fails, but the dump still succeeds. -TEST_F(MemoryDumpManagerTest, PostTaskForSequencedTaskRunner) { - InitializeMemoryDumpManager(false /* is_coordinator */); - std::vector<MockMemoryDumpProvider> mdps(3); - scoped_refptr<TestSequencedTaskRunner> task_runner1( - make_scoped_refptr(new TestSequencedTaskRunner())); - scoped_refptr<TestSequencedTaskRunner> task_runner2( - make_scoped_refptr(new TestSequencedTaskRunner())); - RegisterDumpProviderWithSequencedTaskRunner(&mdps[0], task_runner1, - kDefaultOptions); - RegisterDumpProviderWithSequencedTaskRunner(&mdps[1], task_runner2, - kDefaultOptions); - RegisterDumpProviderWithSequencedTaskRunner(&mdps[2], task_runner2, - kDefaultOptions); - // |mdps[0]| should be disabled permanently after first dump. - EXPECT_CALL(mdps[0], OnMemoryDump(_, _)).Times(0); - EXPECT_CALL(mdps[1], OnMemoryDump(_, _)).Times(2); - EXPECT_CALL(mdps[2], OnMemoryDump(_, _)).Times(2); - EXPECT_CALL(*delegate_, RequestGlobalMemoryDump(_, _)).Times(2); - - EnableTracingWithLegacyCategories(MemoryDumpManager::kTraceCategory); - - task_runner1->set_enabled(false); - last_callback_success_ = false; - RequestGlobalDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED, - MemoryDumpLevelOfDetail::DETAILED); - // Tasks should be individually posted even if |mdps[1]| and |mdps[2]| belong - // to same task runner. - EXPECT_EQ(1u, task_runner1->no_of_post_tasks()); - EXPECT_EQ(2u, task_runner2->no_of_post_tasks()); - EXPECT_TRUE(last_callback_success_); - - task_runner1->set_enabled(true); - last_callback_success_ = false; - RequestGlobalDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED, - MemoryDumpLevelOfDetail::DETAILED); - EXPECT_EQ(2u, task_runner1->no_of_post_tasks()); - EXPECT_EQ(4u, task_runner2->no_of_post_tasks()); - EXPECT_TRUE(last_callback_success_); - DisableTracing(); -} - -// Checks that providers get disabled after 3 consecutive failures, but not -// otherwise (e.g., if interleaved). -TEST_F(MemoryDumpManagerTest, DisableFailingDumpers) { - InitializeMemoryDumpManager(false /* is_coordinator */); - MockMemoryDumpProvider mdp1; - MockMemoryDumpProvider mdp2; - - RegisterDumpProvider(&mdp1, nullptr); - RegisterDumpProvider(&mdp2, nullptr); - EnableTracingWithLegacyCategories(MemoryDumpManager::kTraceCategory); - - const int kNumDumps = 2 * GetMaxConsecutiveFailuresCount(); - EXPECT_CALL(*delegate_, RequestGlobalMemoryDump(_, _)).Times(kNumDumps); - - EXPECT_CALL(mdp1, OnMemoryDump(_, _)) - .Times(GetMaxConsecutiveFailuresCount()) - .WillRepeatedly(Return(false)); - - EXPECT_CALL(mdp2, OnMemoryDump(_, _)) - .WillOnce(Return(false)) - .WillOnce(Return(true)) - .WillOnce(Return(false)) - .WillOnce(Return(false)) - .WillOnce(Return(true)) - .WillOnce(Return(false)); - - for (int i = 0; i < kNumDumps; i++) { - RequestGlobalDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED, - MemoryDumpLevelOfDetail::DETAILED); - } - - DisableTracing(); -} - -// Sneakily registers an extra memory dump provider while an existing one is -// dumping and expect it to take part in the already active tracing session. -TEST_F(MemoryDumpManagerTest, RegisterDumperWhileDumping) { - InitializeMemoryDumpManager(false /* is_coordinator */); - MockMemoryDumpProvider mdp1; - MockMemoryDumpProvider mdp2; - - RegisterDumpProvider(&mdp1, nullptr); - EnableTracingWithLegacyCategories(MemoryDumpManager::kTraceCategory); - - EXPECT_CALL(*delegate_, RequestGlobalMemoryDump(_, _)).Times(4); - - EXPECT_CALL(mdp1, OnMemoryDump(_, _)) - .Times(4) - .WillOnce(Return(true)) - .WillOnce( - Invoke([&mdp2](const MemoryDumpArgs&, ProcessMemoryDump*) -> bool { - RegisterDumpProvider(&mdp2, nullptr); - return true; - })) - .WillRepeatedly(Return(true)); - - // Depending on the insertion order (before or after mdp1), mdp2 might be - // called also immediately after it gets registered. - EXPECT_CALL(mdp2, OnMemoryDump(_, _)) - .Times(Between(2, 3)) - .WillRepeatedly(Return(true)); - - for (int i = 0; i < 4; i++) { - RequestGlobalDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED, - MemoryDumpLevelOfDetail::DETAILED); - } - - DisableTracing(); -} - -// Like RegisterDumperWhileDumping, but unregister the dump provider instead. -TEST_F(MemoryDumpManagerTest, UnregisterDumperWhileDumping) { - InitializeMemoryDumpManager(false /* is_coordinator */); - MockMemoryDumpProvider mdp1; - MockMemoryDumpProvider mdp2; - - RegisterDumpProvider(&mdp1, ThreadTaskRunnerHandle::Get(), kDefaultOptions); - RegisterDumpProvider(&mdp2, ThreadTaskRunnerHandle::Get(), kDefaultOptions); - EnableTracingWithLegacyCategories(MemoryDumpManager::kTraceCategory); - - EXPECT_CALL(*delegate_, RequestGlobalMemoryDump(_, _)).Times(4); - - EXPECT_CALL(mdp1, OnMemoryDump(_, _)) - .Times(4) - .WillOnce(Return(true)) - .WillOnce( - Invoke([&mdp2](const MemoryDumpArgs&, ProcessMemoryDump*) -> bool { - MemoryDumpManager::GetInstance()->UnregisterDumpProvider(&mdp2); - return true; - })) - .WillRepeatedly(Return(true)); - - // Depending on the insertion order (before or after mdp1), mdp2 might have - // been already called when UnregisterDumpProvider happens. - EXPECT_CALL(mdp2, OnMemoryDump(_, _)) - .Times(Between(1, 2)) - .WillRepeatedly(Return(true)); - - for (int i = 0; i < 4; i++) { - RequestGlobalDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED, - MemoryDumpLevelOfDetail::DETAILED); - } - - DisableTracing(); -} - -// Checks that the dump does not abort when unregistering a provider while -// dumping from a different thread than the dumping thread. -TEST_F(MemoryDumpManagerTest, UnregisterDumperFromThreadWhileDumping) { - InitializeMemoryDumpManager(false /* is_coordinator */); - std::vector<std::unique_ptr<TestIOThread>> threads; - std::vector<std::unique_ptr<MockMemoryDumpProvider>> mdps; - - for (int i = 0; i < 2; i++) { - threads.push_back( - WrapUnique(new TestIOThread(TestIOThread::kAutoStart))); - mdps.push_back(WrapUnique(new MockMemoryDumpProvider())); - RegisterDumpProvider(mdps.back().get(), threads.back()->task_runner(), - kDefaultOptions); - } - - int on_memory_dump_call_count = 0; - - // When OnMemoryDump is called on either of the dump providers, it will - // unregister the other one. - for (const std::unique_ptr<MockMemoryDumpProvider>& mdp : mdps) { - int other_idx = (mdps.front() == mdp); - // TestIOThread's task runner must be obtained from the main thread but can - // then be used from other threads. - scoped_refptr<SingleThreadTaskRunner> other_runner = - threads[other_idx]->task_runner(); - MockMemoryDumpProvider* other_mdp = mdps[other_idx].get(); - auto on_dump = [this, other_runner, other_mdp, &on_memory_dump_call_count]( - const MemoryDumpArgs& args, ProcessMemoryDump* pmd) { - PostTaskAndWait(FROM_HERE, other_runner.get(), - base::Bind(&MemoryDumpManager::UnregisterDumpProvider, - base::Unretained(&*mdm_), other_mdp)); - on_memory_dump_call_count++; - return true; - }; - - // OnMemoryDump is called once for the provider that dumps first, and zero - // times for the other provider. - EXPECT_CALL(*mdp, OnMemoryDump(_, _)) - .Times(AtMost(1)) - .WillOnce(Invoke(on_dump)); - } - - last_callback_success_ = false; - EnableTracingWithLegacyCategories(MemoryDumpManager::kTraceCategory); - EXPECT_CALL(*delegate_, RequestGlobalMemoryDump(_, _)).Times(1); - RequestGlobalDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED, - MemoryDumpLevelOfDetail::DETAILED); - ASSERT_EQ(1, on_memory_dump_call_count); - ASSERT_TRUE(last_callback_success_); - - DisableTracing(); -} - -TEST_F(MemoryDumpManagerTest, TestPollingOnDumpThread) { - InitializeMemoryDumpManager(false /* is_coordinator */); - std::unique_ptr<MockMemoryDumpProvider> mdp1(new MockMemoryDumpProvider()); - std::unique_ptr<MockMemoryDumpProvider> mdp2(new MockMemoryDumpProvider()); - mdp1->enable_mock_destructor = true; - mdp2->enable_mock_destructor = true; - - EXPECT_CALL(*mdp1, SuspendFastMemoryPolling()).Times(1); - EXPECT_CALL(*mdp2, SuspendFastMemoryPolling()).Times(1); - EXPECT_CALL(*mdp1, Destructor()); - EXPECT_CALL(*mdp2, Destructor()); - - MemoryDumpProvider::Options options; - options.is_fast_polling_supported = true; - RegisterDumpProvider(mdp1.get(), nullptr, options); - - RunLoop run_loop; - scoped_refptr<SingleThreadTaskRunner> test_task_runner = - ThreadTaskRunnerHandle::Get(); - auto quit_closure = run_loop.QuitClosure(); - - const int kPollsToQuit = 10; - int call_count = 0; - MemoryDumpManager* mdm = mdm_.get(); - const auto poll_function1 = [&call_count, &test_task_runner, quit_closure, - &mdp2, mdm, &options, kPollsToQuit, - this](uint64_t* total) -> void { - ++call_count; - if (call_count == 1) - RegisterDumpProvider(mdp2.get(), nullptr, options, kMDPName); - else if (call_count == 4) - mdm->UnregisterAndDeleteDumpProviderSoon(std::move(mdp2)); - else if (call_count == kPollsToQuit) - test_task_runner->PostTask(FROM_HERE, quit_closure); - - // Record increase of 1 GiB of memory at each call. - *total = static_cast<uint64_t>(call_count) * 1024 * 1024 * 1024; - }; - EXPECT_CALL(*mdp1, PollFastMemoryTotal(_)) - .Times(testing::AtLeast(kPollsToQuit)) - .WillRepeatedly(Invoke(poll_function1)); - - // Depending on the order of PostTask calls the mdp2 might be registered after - // all polls or in between polls. - EXPECT_CALL(*mdp2, PollFastMemoryTotal(_)) - .Times(Between(0, kPollsToQuit - 1)) - .WillRepeatedly(Return()); - - MemoryDumpScheduler::SetPollingIntervalForTesting(1); - EnableTracingWithTraceConfig( - TraceConfigMemoryTestUtil::GetTraceConfig_PeakDetectionTrigger(3)); - - int last_poll_to_request_dump = -2; - EXPECT_CALL(*delegate_, RequestGlobalMemoryDump(_, _)) - .Times(testing::AtLeast(2)) - .WillRepeatedly(Invoke([&last_poll_to_request_dump, &call_count]( - const MemoryDumpRequestArgs& args, - const MemoryDumpCallback& callback) -> void { - // Minimum number of polls between dumps must be 3 (polling interval is - // 1ms). - EXPECT_GE(call_count - last_poll_to_request_dump, 3); - last_poll_to_request_dump = call_count; - })); - - run_loop.Run(); - DisableTracing(); - mdm_->UnregisterAndDeleteDumpProviderSoon(std::move(mdp1)); -} - -// If a thread (with a dump provider living on it) is torn down during a dump -// its dump provider should be skipped but the dump itself should succeed. -TEST_F(MemoryDumpManagerTest, TearDownThreadWhileDumping) { - InitializeMemoryDumpManager(false /* is_coordinator */); - std::vector<std::unique_ptr<TestIOThread>> threads; - std::vector<std::unique_ptr<MockMemoryDumpProvider>> mdps; - - for (int i = 0; i < 2; i++) { - threads.push_back( - WrapUnique(new TestIOThread(TestIOThread::kAutoStart))); - mdps.push_back(WrapUnique(new MockMemoryDumpProvider())); - RegisterDumpProvider(mdps.back().get(), threads.back()->task_runner(), - kDefaultOptions); - } - - int on_memory_dump_call_count = 0; - - // When OnMemoryDump is called on either of the dump providers, it will - // tear down the thread of the other one. - for (const std::unique_ptr<MockMemoryDumpProvider>& mdp : mdps) { - int other_idx = (mdps.front() == mdp); - TestIOThread* other_thread = threads[other_idx].get(); - // TestIOThread isn't thread-safe and must be stopped on the |main_runner|. - scoped_refptr<SequencedTaskRunner> main_runner = - SequencedTaskRunnerHandle::Get(); - auto on_dump = [other_thread, main_runner, &on_memory_dump_call_count]( - const MemoryDumpArgs& args, ProcessMemoryDump* pmd) { - PostTaskAndWait( - FROM_HERE, main_runner.get(), - base::Bind(&TestIOThread::Stop, base::Unretained(other_thread))); - on_memory_dump_call_count++; - return true; - }; - - // OnMemoryDump is called once for the provider that dumps first, and zero - // times for the other provider. - EXPECT_CALL(*mdp, OnMemoryDump(_, _)) - .Times(AtMost(1)) - .WillOnce(Invoke(on_dump)); - } - - last_callback_success_ = false; - EnableTracingWithLegacyCategories(MemoryDumpManager::kTraceCategory); - EXPECT_CALL(*delegate_, RequestGlobalMemoryDump(_, _)).Times(1); - RequestGlobalDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED, - MemoryDumpLevelOfDetail::DETAILED); - ASSERT_EQ(1, on_memory_dump_call_count); - ASSERT_TRUE(last_callback_success_); - - DisableTracing(); -} - -// Checks that a NACK callback is invoked if RequestGlobalDump() is called when -// tracing is not enabled. -TEST_F(MemoryDumpManagerTest, CallbackCalledOnFailure) { - InitializeMemoryDumpManager(false /* is_coordinator */); - MockMemoryDumpProvider mdp1; - RegisterDumpProvider(&mdp1, nullptr); - - EXPECT_CALL(*delegate_, RequestGlobalMemoryDump(_, _)).Times(0); - EXPECT_CALL(mdp1, OnMemoryDump(_, _)).Times(0); - - last_callback_success_ = true; - RequestGlobalDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED, - MemoryDumpLevelOfDetail::DETAILED); - EXPECT_FALSE(last_callback_success_); -} - -// Checks that is the MemoryDumpManager is initialized after tracing already -// began, it will still late-join the party (real use case: startup tracing). -TEST_F(MemoryDumpManagerTest, InitializedAfterStartOfTracing) { - MockMemoryDumpProvider mdp; - RegisterDumpProvider(&mdp, nullptr); - EnableTracingWithLegacyCategories(MemoryDumpManager::kTraceCategory); - - // First check that a RequestGlobalDump() issued before the MemoryDumpManager - // initialization gets NACK-ed cleanly. - { - EXPECT_CALL(mdp, OnMemoryDump(_, _)).Times(0); - RequestGlobalDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED, - MemoryDumpLevelOfDetail::DETAILED); - EXPECT_FALSE(last_callback_success_); - } - - // Now late-initialize the MemoryDumpManager and check that the - // RequestGlobalDump completes successfully. - { - InitializeMemoryDumpManager(false /* is_coordinator */); - EXPECT_CALL(mdp, OnMemoryDump(_, _)).Times(1); - EXPECT_CALL(*delegate_, RequestGlobalMemoryDump(_, _)).Times(1); - RequestGlobalDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED, - MemoryDumpLevelOfDetail::DETAILED); - EXPECT_TRUE(last_callback_success_); - } - DisableTracing(); -} - -// This test (and the MemoryDumpManagerTestCoordinator below) crystallizes the -// expectations of the chrome://tracing UI and chrome telemetry w.r.t. periodic -// dumps in memory-infra, handling gracefully the transition between the legacy -// and the new-style (JSON-based) TraceConfig. -TEST_F(MemoryDumpManagerTest, TraceConfigExpectations) { - InitializeMemoryDumpManager(false /* is_coordinator */); - MemoryDumpManagerDelegateForTesting& delegate = *delegate_; - - // Don't trigger the default behavior of the mock delegate in this test, - // which would short-circuit the dump request to the actual - // CreateProcessDump(). - // We don't want to create any dump in this test, only check whether the dumps - // are requested or not. - ON_CALL(delegate, RequestGlobalMemoryDump(_, _)).WillByDefault(Return()); - - // Enabling memory-infra in a non-coordinator process should not trigger any - // periodic dumps. - EnableTracingWithLegacyCategories(MemoryDumpManager::kTraceCategory); - EXPECT_FALSE(IsPeriodicDumpingEnabled()); - DisableTracing(); - - // Enabling memory-infra with the new (JSON) TraceConfig in a non-coordinator - // process with a fully defined trigger config should NOT enable any periodic - // dumps. - EnableTracingWithTraceConfig( - TraceConfigMemoryTestUtil::GetTraceConfig_PeriodicTriggers(1, 5)); - EXPECT_FALSE(IsPeriodicDumpingEnabled()); - DisableTracing(); -} - -TEST_F(MemoryDumpManagerTest, TraceConfigExpectationsWhenIsCoordinator) { - InitializeMemoryDumpManager(true /* is_coordinator */); - MemoryDumpManagerDelegateForTesting& delegate = *delegate_; - ON_CALL(delegate, RequestGlobalMemoryDump(_, _)).WillByDefault(Return()); - - // Enabling memory-infra with the legacy TraceConfig (category filter) in - // a coordinator process should enable periodic dumps. - EnableTracingWithLegacyCategories(MemoryDumpManager::kTraceCategory); - EXPECT_TRUE(IsPeriodicDumpingEnabled()); - DisableTracing(); - - // Enabling memory-infra with the new (JSON) TraceConfig in a coordinator - // process without specifying any "memory_dump_config" section should enable - // periodic dumps. This is to preserve the behavior chrome://tracing UI, that - // is: ticking memory-infra should dump periodically with the default config. - EnableTracingWithTraceConfig( - TraceConfigMemoryTestUtil::GetTraceConfig_NoTriggers()); - EXPECT_TRUE(IsPeriodicDumpingEnabled()); - DisableTracing(); - - // Enabling memory-infra with the new (JSON) TraceConfig in a coordinator - // process with an empty "memory_dump_config" should NOT enable periodic - // dumps. This is the way telemetry is supposed to use memory-infra with - // only explicitly triggered dumps. - EnableTracingWithTraceConfig( - TraceConfigMemoryTestUtil::GetTraceConfig_EmptyTriggers()); - EXPECT_FALSE(IsPeriodicDumpingEnabled()); - DisableTracing(); - - // Enabling memory-infra with the new (JSON) TraceConfig in a coordinator - // process with a fully defined trigger config should cause periodic dumps to - // be performed in the correct order. - RunLoop run_loop; - auto quit_closure = run_loop.QuitClosure(); - - const int kHeavyDumpRate = 5; - const int kLightDumpPeriodMs = 1; - const int kHeavyDumpPeriodMs = kHeavyDumpRate * kLightDumpPeriodMs; - // The expected sequence with light=1ms, heavy=5ms is H,L,L,L,L,H,... - testing::InSequence sequence; - EXPECT_CALL(delegate, RequestGlobalMemoryDump(IsDetailedDump(), _)); - EXPECT_CALL(delegate, RequestGlobalMemoryDump(IsLightDump(), _)) - .Times(kHeavyDumpRate - 1); - EXPECT_CALL(delegate, RequestGlobalMemoryDump(IsDetailedDump(), _)); - EXPECT_CALL(delegate, RequestGlobalMemoryDump(IsLightDump(), _)) - .Times(kHeavyDumpRate - 2); - EXPECT_CALL(delegate, RequestGlobalMemoryDump(IsLightDump(), _)) - .WillOnce(Invoke([quit_closure](const MemoryDumpRequestArgs& args, - const MemoryDumpCallback& callback) { - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, quit_closure); - })); - - // Swallow all the final spurious calls until tracing gets disabled. - EXPECT_CALL(delegate, RequestGlobalMemoryDump(_, _)).Times(AnyNumber()); - - EnableTracingWithTraceConfig( - TraceConfigMemoryTestUtil::GetTraceConfig_PeriodicTriggers( - kLightDumpPeriodMs, kHeavyDumpPeriodMs)); - run_loop.Run(); - DisableTracing(); -} - -// Tests against race conditions that might arise when disabling tracing in the -// middle of a global memory dump. -// Flaky on iOS, see crbug.com/706961 -#if defined(OS_IOS) -#define MAYBE_DisableTracingWhileDumping DISABLED_DisableTracingWhileDumping -#else -#define MAYBE_DisableTracingWhileDumping DisableTracingWhileDumping -#endif -TEST_F(MemoryDumpManagerTest, MAYBE_DisableTracingWhileDumping) { - base::WaitableEvent tracing_disabled_event( - WaitableEvent::ResetPolicy::AUTOMATIC, - WaitableEvent::InitialState::NOT_SIGNALED); - InitializeMemoryDumpManager(false /* is_coordinator */); - - // Register a bound dump provider. - std::unique_ptr<Thread> mdp_thread(new Thread("test thread")); - mdp_thread->Start(); - MockMemoryDumpProvider mdp_with_affinity; - RegisterDumpProvider(&mdp_with_affinity, mdp_thread->task_runner(), - kDefaultOptions); - - // Register also an unbound dump provider. Unbound dump providers are always - // invoked after bound ones. - MockMemoryDumpProvider unbound_mdp; - RegisterDumpProvider(&unbound_mdp, nullptr, kDefaultOptions); - - EnableTracingWithLegacyCategories(MemoryDumpManager::kTraceCategory); - EXPECT_CALL(*delegate_, RequestGlobalMemoryDump(_, _)).Times(1); - EXPECT_CALL(mdp_with_affinity, OnMemoryDump(_, _)) - .Times(1) - .WillOnce( - Invoke([&tracing_disabled_event](const MemoryDumpArgs&, - ProcessMemoryDump* pmd) -> bool { - tracing_disabled_event.Wait(); - - // At this point tracing has been disabled and the - // MemoryDumpManager.dump_thread_ has been shut down. - return true; - })); - - // |unbound_mdp| should never be invoked because the thread for unbound dump - // providers has been shutdown in the meanwhile. - EXPECT_CALL(unbound_mdp, OnMemoryDump(_, _)).Times(0); - - last_callback_success_ = true; - RunLoop run_loop; - MemoryDumpCallback callback = - Bind(&MemoryDumpManagerTest::DumpCallbackAdapter, Unretained(this), - ThreadTaskRunnerHandle::Get(), run_loop.QuitClosure()); - mdm_->RequestGlobalDump(MemoryDumpType::EXPLICITLY_TRIGGERED, - MemoryDumpLevelOfDetail::DETAILED, callback); - DisableTracing(); - tracing_disabled_event.Signal(); - run_loop.Run(); - - EXPECT_FALSE(last_callback_success_); -} - -// Tests against race conditions that can happen if tracing is disabled before -// the CreateProcessDump() call. Real-world regression: crbug.com/580295 . -TEST_F(MemoryDumpManagerTest, DisableTracingRightBeforeStartOfDump) { - base::WaitableEvent tracing_disabled_event( - WaitableEvent::ResetPolicy::AUTOMATIC, - WaitableEvent::InitialState::NOT_SIGNALED); - InitializeMemoryDumpManager(false /* is_coordinator */); - - std::unique_ptr<Thread> mdp_thread(new Thread("test thread")); - mdp_thread->Start(); - - // Create both same-thread MDP and another MDP with dedicated thread - MockMemoryDumpProvider mdp1; - RegisterDumpProvider(&mdp1, nullptr); - MockMemoryDumpProvider mdp2; - RegisterDumpProvider(&mdp2, mdp_thread->task_runner(), kDefaultOptions); - EnableTracingWithLegacyCategories(MemoryDumpManager::kTraceCategory); - - EXPECT_CALL(*delegate_, RequestGlobalMemoryDump(_, _)) - .WillOnce(Invoke([this](const MemoryDumpRequestArgs& args, - const MemoryDumpCallback& callback) { - DisableTracing(); - delegate_->CreateProcessDump(args, callback); - })); - - // If tracing is disabled for current session CreateProcessDump() should NOT - // request dumps from providers. Real-world regression: crbug.com/600570 . - EXPECT_CALL(mdp1, OnMemoryDump(_, _)).Times(0); - EXPECT_CALL(mdp2, OnMemoryDump(_, _)).Times(0); - - last_callback_success_ = true; - RequestGlobalDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED, - MemoryDumpLevelOfDetail::DETAILED); - EXPECT_FALSE(last_callback_success_); -} - -TEST_F(MemoryDumpManagerTest, DumpOnBehalfOfOtherProcess) { - using trace_analyzer::Query; - - InitializeMemoryDumpManager(false /* is_coordinator */); - - // Standard provider with default options (create dump for current process). - MemoryDumpProvider::Options options; - MockMemoryDumpProvider mdp1; - RegisterDumpProvider(&mdp1, nullptr, options); - - // Provider with out-of-process dumping. - MockMemoryDumpProvider mdp2; - options.target_pid = 123; - RegisterDumpProvider(&mdp2, nullptr, options); - - // Another provider with out-of-process dumping. - MockMemoryDumpProvider mdp3; - options.target_pid = 456; - RegisterDumpProvider(&mdp3, nullptr, options); - - EnableTracingWithLegacyCategories(MemoryDumpManager::kTraceCategory); - EXPECT_CALL(*delegate_, RequestGlobalMemoryDump(_, _)).Times(1); - EXPECT_CALL(mdp1, OnMemoryDump(_, _)).Times(1).WillRepeatedly(Return(true)); - EXPECT_CALL(mdp2, OnMemoryDump(_, _)).Times(1).WillRepeatedly(Return(true)); - EXPECT_CALL(mdp3, OnMemoryDump(_, _)).Times(1).WillRepeatedly(Return(true)); - RequestGlobalDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED, - MemoryDumpLevelOfDetail::DETAILED); - DisableTracing(); - - // Flush the trace into JSON. - trace_event::TraceResultBuffer buffer; - TraceResultBuffer::SimpleOutput trace_output; - buffer.SetOutputCallback(trace_output.GetCallback()); - RunLoop run_loop; - buffer.Start(); - trace_event::TraceLog::GetInstance()->Flush( - Bind(&OnTraceDataCollected, run_loop.QuitClosure(), Unretained(&buffer))); - run_loop.Run(); - buffer.Finish(); - - // Analyze the JSON. - std::unique_ptr<trace_analyzer::TraceAnalyzer> analyzer = WrapUnique( - trace_analyzer::TraceAnalyzer::Create(trace_output.json_output)); - trace_analyzer::TraceEventVector events; - analyzer->FindEvents(Query::EventPhaseIs(TRACE_EVENT_PHASE_MEMORY_DUMP), - &events); - - ASSERT_EQ(3u, events.size()); - ASSERT_EQ(1u, trace_analyzer::CountMatches(events, Query::EventPidIs(123))); - ASSERT_EQ(1u, trace_analyzer::CountMatches(events, Query::EventPidIs(456))); - ASSERT_EQ(1u, trace_analyzer::CountMatches( - events, Query::EventPidIs(GetCurrentProcId()))); - ASSERT_EQ(events[0]->id, events[1]->id); - ASSERT_EQ(events[0]->id, events[2]->id); -} - -// Tests the basics of the UnregisterAndDeleteDumpProviderSoon(): the -// unregistration should actually delete the providers and not leak them. -TEST_F(MemoryDumpManagerTest, UnregisterAndDeleteDumpProviderSoon) { - InitializeMemoryDumpManager(false /* is_coordinator */); - static const int kNumProviders = 3; - int dtor_count = 0; - std::vector<std::unique_ptr<MemoryDumpProvider>> mdps; - for (int i = 0; i < kNumProviders; ++i) { - std::unique_ptr<MockMemoryDumpProvider> mdp(new MockMemoryDumpProvider); - mdp->enable_mock_destructor = true; - EXPECT_CALL(*mdp, Destructor()) - .WillOnce(Invoke([&dtor_count]() { dtor_count++; })); - RegisterDumpProvider(mdp.get(), nullptr, kDefaultOptions); - mdps.push_back(std::move(mdp)); - } - - while (!mdps.empty()) { - mdm_->UnregisterAndDeleteDumpProviderSoon(std::move(mdps.back())); - mdps.pop_back(); - } - - ASSERT_EQ(kNumProviders, dtor_count); -} - -// This test checks against races when unregistering an unbound dump provider -// from another thread while dumping. It registers one MDP and, when -// OnMemoryDump() is called, it invokes UnregisterAndDeleteDumpProviderSoon() -// from another thread. The OnMemoryDump() and the dtor call are expected to -// happen on the same thread (the MemoryDumpManager utility thread). -TEST_F(MemoryDumpManagerTest, UnregisterAndDeleteDumpProviderSoonDuringDump) { - InitializeMemoryDumpManager(false /* is_coordinator */); - std::unique_ptr<MockMemoryDumpProvider> mdp(new MockMemoryDumpProvider); - mdp->enable_mock_destructor = true; - RegisterDumpProvider(mdp.get(), nullptr, kDefaultOptions); - - base::PlatformThreadRef thread_ref; - auto self_unregister_from_another_thread = [&mdp, &thread_ref]( - const MemoryDumpArgs&, ProcessMemoryDump*) -> bool { - thread_ref = PlatformThread::CurrentRef(); - TestIOThread thread_for_unregistration(TestIOThread::kAutoStart); - PostTaskAndWait( - FROM_HERE, thread_for_unregistration.task_runner().get(), - base::Bind( - &MemoryDumpManager::UnregisterAndDeleteDumpProviderSoon, - base::Unretained(MemoryDumpManager::GetInstance()), - base::Passed(std::unique_ptr<MemoryDumpProvider>(std::move(mdp))))); - thread_for_unregistration.Stop(); - return true; - }; - EXPECT_CALL(*mdp, OnMemoryDump(_, _)) - .Times(1) - .WillOnce(Invoke(self_unregister_from_another_thread)); - EXPECT_CALL(*mdp, Destructor()) - .Times(1) - .WillOnce(Invoke([&thread_ref]() { - EXPECT_EQ(thread_ref, PlatformThread::CurrentRef()); - })); - - EnableTracingWithLegacyCategories(MemoryDumpManager::kTraceCategory); - EXPECT_CALL(*delegate_, RequestGlobalMemoryDump(_, _)).Times(2); - for (int i = 0; i < 2; ++i) { - RequestGlobalDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED, - MemoryDumpLevelOfDetail::DETAILED); - } - DisableTracing(); -} - -TEST_F(MemoryDumpManagerTest, TestWhitelistingMDP) { - InitializeMemoryDumpManager(false /* is_coordinator */); - SetDumpProviderWhitelistForTesting(kTestMDPWhitelist); - std::unique_ptr<MockMemoryDumpProvider> mdp1(new MockMemoryDumpProvider); - RegisterDumpProvider(mdp1.get(), nullptr); - std::unique_ptr<MockMemoryDumpProvider> mdp2(new MockMemoryDumpProvider); - RegisterDumpProvider(mdp2.get(), nullptr, kDefaultOptions, - kWhitelistedMDPName); - - EXPECT_CALL(*mdp1, OnMemoryDump(_, _)).Times(0); - EXPECT_CALL(*mdp2, OnMemoryDump(_, _)).Times(1).WillOnce(Return(true)); - EXPECT_CALL(*delegate_, RequestGlobalMemoryDump(_, _)).Times(1); - - EnableTracingWithLegacyCategories(MemoryDumpManager::kTraceCategory); - EXPECT_FALSE(IsPeriodicDumpingEnabled()); - RequestGlobalDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED, - MemoryDumpLevelOfDetail::BACKGROUND); - DisableTracing(); -} - -TEST_F(MemoryDumpManagerTest, TestBackgroundTracingSetup) { - InitializeMemoryDumpManager(true /* is_coordinator */); - - RunLoop run_loop; - auto quit_closure = run_loop.QuitClosure(); - - testing::InSequence sequence; - EXPECT_CALL(*delegate_, RequestGlobalMemoryDump(IsBackgroundDump(), _)) - .Times(5); - EXPECT_CALL(*delegate_, RequestGlobalMemoryDump(IsBackgroundDump(), _)) - .WillOnce(Invoke([quit_closure](const MemoryDumpRequestArgs& args, - const MemoryDumpCallback& callback) { - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, quit_closure); - })); - EXPECT_CALL(*delegate_, RequestGlobalMemoryDump(_, _)).Times(AnyNumber()); - - EnableTracingWithTraceConfig( - TraceConfigMemoryTestUtil::GetTraceConfig_BackgroundTrigger( - 1 /* period_ms */)); - - // Only background mode dumps should be allowed with the trace config. - last_callback_success_ = false; - RequestGlobalDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED, - MemoryDumpLevelOfDetail::LIGHT); - EXPECT_FALSE(last_callback_success_); - last_callback_success_ = false; - RequestGlobalDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED, - MemoryDumpLevelOfDetail::DETAILED); - EXPECT_FALSE(last_callback_success_); - - ASSERT_TRUE(IsPeriodicDumpingEnabled()); - run_loop.Run(); - DisableTracing(); -} - -TEST_F(MemoryDumpManagerTest, TestBlacklistedUnsafeUnregistration) { - InitializeMemoryDumpManager(false /* is_coordinator */); - MockMemoryDumpProvider mdp1; - RegisterDumpProvider(&mdp1, nullptr, kDefaultOptions, - "BlacklistTestDumpProvider"); - // Not calling UnregisterAndDeleteDumpProviderSoon() should not crash. - mdm_->UnregisterDumpProvider(&mdp1); - - Thread thread("test thread"); - thread.Start(); - RegisterDumpProvider(&mdp1, thread.task_runner(), kDefaultOptions, - "BlacklistTestDumpProvider"); - // Unregistering on wrong thread should not crash. - mdm_->UnregisterDumpProvider(&mdp1); - thread.Stop(); -} - -} // namespace trace_event -} // namespace base diff --git a/base/trace_event/memory_dump_provider.h b/base/trace_event/memory_dump_provider.h deleted file mode 100644 index 244319efa7..0000000000 --- a/base/trace_event/memory_dump_provider.h +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_TRACE_EVENT_MEMORY_DUMP_PROVIDER_H_ -#define BASE_TRACE_EVENT_MEMORY_DUMP_PROVIDER_H_ - -#include "base/base_export.h" -#include "base/macros.h" -#include "base/process/process_handle.h" -#include "base/trace_event/memory_dump_request_args.h" - -namespace base { -namespace trace_event { - -class ProcessMemoryDump; - -// The contract interface that memory dump providers must implement. -class BASE_EXPORT MemoryDumpProvider { - public: - // Optional arguments for MemoryDumpManager::RegisterDumpProvider(). - struct Options { - Options() - : target_pid(kNullProcessId), - dumps_on_single_thread_task_runner(false), - is_fast_polling_supported(false) {} - - // If the dump provider generates dumps on behalf of another process, - // |target_pid| contains the pid of that process. - // The default value is kNullProcessId, which means that the dump provider - // generates dumps for the current process. - ProcessId target_pid; - - // |dumps_on_single_thread_task_runner| is true if the dump provider runs on - // a SingleThreadTaskRunner, which is usually the case. It is faster to run - // all providers that run on the same thread together without thread hops. - bool dumps_on_single_thread_task_runner; - - // Set to true if the dump provider implementation supports high frequency - // polling. Only providers running without task runner affinity are - // supported. - bool is_fast_polling_supported; - }; - - virtual ~MemoryDumpProvider() {} - - // Called by the MemoryDumpManager when generating memory dumps. - // The |args| specify if the embedder should generate light/heavy dumps on - // dump requests. The embedder should return true if the |pmd| was - // successfully populated, false if something went wrong and the dump should - // be considered invalid. - // (Note, the MemoryDumpManager has a fail-safe logic which will disable the - // MemoryDumpProvider for the entire trace session if it fails consistently). - virtual bool OnMemoryDump(const MemoryDumpArgs& args, - ProcessMemoryDump* pmd) = 0; - - // Called by the MemoryDumpManager when an allocator should start or stop - // collecting extensive allocation data, if supported. - virtual void OnHeapProfilingEnabled(bool enabled) {} - - // Quickly record the total memory usage in |memory_total|. This method will - // be called only when the dump provider registration has - // |is_fast_polling_supported| set to true. This method is used for polling at - // high frequency for detecting peaks. See comment on - // |is_fast_polling_supported| option if you need to override this method. - virtual void PollFastMemoryTotal(uint64_t* memory_total) {} - - // Indicates that fast memory polling is not going to be used in the near - // future and the MDP can tear down any resource kept around for fast memory - // polling. - virtual void SuspendFastMemoryPolling() {} - - protected: - MemoryDumpProvider() {} - - DISALLOW_COPY_AND_ASSIGN(MemoryDumpProvider); -}; - -} // namespace trace_event -} // namespace base - -#endif // BASE_TRACE_EVENT_MEMORY_DUMP_PROVIDER_H_ diff --git a/base/trace_event/memory_dump_provider_info.cc b/base/trace_event/memory_dump_provider_info.cc deleted file mode 100644 index 6bb711018b..0000000000 --- a/base/trace_event/memory_dump_provider_info.cc +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/trace_event/memory_dump_provider_info.h" - -#include <tuple> - -#include "base/sequenced_task_runner.h" - -namespace base { -namespace trace_event { - -MemoryDumpProviderInfo::MemoryDumpProviderInfo( - MemoryDumpProvider* dump_provider, - const char* name, - scoped_refptr<SequencedTaskRunner> task_runner, - const MemoryDumpProvider::Options& options, - bool whitelisted_for_background_mode) - : dump_provider(dump_provider), - options(options), - name(name), - task_runner(std::move(task_runner)), - whitelisted_for_background_mode(whitelisted_for_background_mode), - consecutive_failures(0), - disabled(false) {} - -MemoryDumpProviderInfo::~MemoryDumpProviderInfo() {} - -bool MemoryDumpProviderInfo::Comparator::operator()( - const scoped_refptr<MemoryDumpProviderInfo>& a, - const scoped_refptr<MemoryDumpProviderInfo>& b) const { - if (!a || !b) - return a.get() < b.get(); - // Ensure that unbound providers (task_runner == nullptr) always run last. - // Rationale: some unbound dump providers are known to be slow, keep them last - // to avoid skewing timings of the other dump providers. - return std::tie(a->task_runner, a->dump_provider) > - std::tie(b->task_runner, b->dump_provider); -} - -} // namespace trace_event -} // namespace base diff --git a/base/trace_event/memory_dump_provider_info.h b/base/trace_event/memory_dump_provider_info.h deleted file mode 100644 index ca63a987b2..0000000000 --- a/base/trace_event/memory_dump_provider_info.h +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_TRACE_EVENT_MEMORY_DUMP_PROVIDER_INFO_H_ -#define BASE_TRACE_EVENT_MEMORY_DUMP_PROVIDER_INFO_H_ - -#include <memory> -#include <set> - -#include "base/base_export.h" -#include "base/memory/ref_counted.h" -#include "base/trace_event/memory_dump_provider.h" - -namespace base { - -class SequencedTaskRunner; - -namespace trace_event { - -// Wraps a MemoryDumpProvider (MDP), which is registered via -// MemoryDumpManager(MDM)::RegisterDumpProvider(), holding the extra information -// required to deal with it (which task runner it should be invoked onto, -// whether it has been disabled, etc.) -// More importantly, having a refptr to this object guarantees that a MDP that -// is not thread-bound (hence which can only be unregistered via -// MDM::UnregisterAndDeleteDumpProviderSoon()) will stay alive as long as the -// refptr is held. -// -// Lifetime: -// At any time, there is at most one instance of this class for each instance -// of a given MemoryDumpProvider, but there might be several scoped_refptr -// holding onto each of this. Specifically: -// - In nominal conditions, there is a refptr for each registerd MDP in the -// MDM's |dump_providers_| list. -// - In most cases, the only refptr (in the |dump_providers_| list) is destroyed -// by MDM::UnregisterDumpProvider(). -// - However, when MDM starts a dump, the list of refptrs is copied into the -// ProcessMemoryDumpAsyncState. That list is pruned as MDP(s) are invoked. -// - If UnregisterDumpProvider() is called on a non-thread-bound MDP while a -// dump is in progress, the extar extra of the handle is destroyed in -// MDM::SetupNextMemoryDump() or MDM::InvokeOnMemoryDump(), when the copy -// inside ProcessMemoryDumpAsyncState is erase()-d. -// - The PeakDetector can keep extra refptrs when enabled. -struct BASE_EXPORT MemoryDumpProviderInfo - : public RefCountedThreadSafe<MemoryDumpProviderInfo> { - public: - // Define a total order based on the |task_runner| affinity, so that MDPs - // belonging to the same SequencedTaskRunner are adjacent in the set. - struct Comparator { - bool operator()(const scoped_refptr<MemoryDumpProviderInfo>& a, - const scoped_refptr<MemoryDumpProviderInfo>& b) const; - }; - using OrderedSet = - std::set<scoped_refptr<MemoryDumpProviderInfo>, Comparator>; - - MemoryDumpProviderInfo(MemoryDumpProvider* dump_provider, - const char* name, - scoped_refptr<SequencedTaskRunner> task_runner, - const MemoryDumpProvider::Options& options, - bool whitelisted_for_background_mode); - - // It is safe to access the const fields below from any thread as they are - // never mutated. - - MemoryDumpProvider* const dump_provider; - - // The |options| arg passed to MDM::RegisterDumpProvider(). - const MemoryDumpProvider::Options options; - - // Human readable name, not unique (distinct MDP instances might have the same - // name). Used for debugging, testing and whitelisting for BACKGROUND mode. - const char* const name; - - // The task runner on which the MDP::OnMemoryDump call should be posted onto. - // Can be nullptr, in which case the MDP will be invoked on a background - // thread handled by MDM. - const scoped_refptr<SequencedTaskRunner> task_runner; - - // True if the dump provider is whitelisted for background mode. - const bool whitelisted_for_background_mode; - - // These fields below, instead, are not thread safe and can be mutated only: - // - On the |task_runner|, when not null (i.e. for thread-bound MDPS). - // - By the MDM's background thread (or in any other way that guarantees - // sequencing) for non-thread-bound MDPs. - - // Used to transfer ownership for UnregisterAndDeleteDumpProviderSoon(). - // nullptr in all other cases. - std::unique_ptr<MemoryDumpProvider> owned_dump_provider; - - // For fail-safe logic (auto-disable failing MDPs). - int consecutive_failures; - - // Flagged either by the auto-disable logic or during unregistration. - bool disabled; - - private: - friend class base::RefCountedThreadSafe<MemoryDumpProviderInfo>; - ~MemoryDumpProviderInfo(); - - DISALLOW_COPY_AND_ASSIGN(MemoryDumpProviderInfo); -}; - -} // namespace trace_event -} // namespace base - -#endif // BASE_TRACE_EVENT_MEMORY_DUMP_PROVIDER_INFO_H_ diff --git a/base/trace_event/memory_dump_request_args.cc b/base/trace_event/memory_dump_request_args.cc deleted file mode 100644 index f2744007d7..0000000000 --- a/base/trace_event/memory_dump_request_args.cc +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/trace_event/memory_dump_request_args.h" - -#include "base/logging.h" - -namespace base { -namespace trace_event { - -// static -const char* MemoryDumpTypeToString(const MemoryDumpType& dump_type) { - switch (dump_type) { - case MemoryDumpType::PERIODIC_INTERVAL: - return "periodic_interval"; - case MemoryDumpType::EXPLICITLY_TRIGGERED: - return "explicitly_triggered"; - case MemoryDumpType::PEAK_MEMORY_USAGE: - return "peak_memory_usage"; - } - NOTREACHED(); - return "unknown"; -} - -MemoryDumpType StringToMemoryDumpType(const std::string& str) { - if (str == "periodic_interval") - return MemoryDumpType::PERIODIC_INTERVAL; - if (str == "explicitly_triggered") - return MemoryDumpType::EXPLICITLY_TRIGGERED; - if (str == "peak_memory_usage") - return MemoryDumpType::PEAK_MEMORY_USAGE; - NOTREACHED(); - return MemoryDumpType::LAST; -} - -const char* MemoryDumpLevelOfDetailToString( - const MemoryDumpLevelOfDetail& level_of_detail) { - switch (level_of_detail) { - case MemoryDumpLevelOfDetail::BACKGROUND: - return "background"; - case MemoryDumpLevelOfDetail::LIGHT: - return "light"; - case MemoryDumpLevelOfDetail::DETAILED: - return "detailed"; - } - NOTREACHED(); - return "unknown"; -} - -MemoryDumpLevelOfDetail StringToMemoryDumpLevelOfDetail( - const std::string& str) { - if (str == "background") - return MemoryDumpLevelOfDetail::BACKGROUND; - if (str == "light") - return MemoryDumpLevelOfDetail::LIGHT; - if (str == "detailed") - return MemoryDumpLevelOfDetail::DETAILED; - NOTREACHED(); - return MemoryDumpLevelOfDetail::LAST; -} - -MemoryDumpCallbackResult::MemoryDumpCallbackResult() {} - -MemoryDumpCallbackResult::~MemoryDumpCallbackResult() {} - -} // namespace trace_event -} // namespace base diff --git a/base/trace_event/memory_dump_request_args.h b/base/trace_event/memory_dump_request_args.h deleted file mode 100644 index a8b3f423ca..0000000000 --- a/base/trace_event/memory_dump_request_args.h +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_TRACE_EVENT_MEMORY_DUMP_REQUEST_ARGS_H_ -#define BASE_TRACE_EVENT_MEMORY_DUMP_REQUEST_ARGS_H_ - -// This file defines the types and structs used to issue memory dump requests. -// These are also used in the IPCs for coordinating inter-process memory dumps. - -#include <stdint.h> -#include <map> -#include <string> - -#include "base/base_export.h" -#include "base/callback.h" -#include "base/process/process_handle.h" - -namespace base { -namespace trace_event { - -// Captures the reason why a memory dump is being requested. This is to allow -// selective enabling of dumps, filtering and post-processing. Important: this -// must be kept consistent with -// services/resource_coordinator/public/cpp/memory/memory_infra_traits.cc. -enum class MemoryDumpType { - PERIODIC_INTERVAL, // Dumping memory at periodic intervals. - EXPLICITLY_TRIGGERED, // Non maskable dump request. - PEAK_MEMORY_USAGE, // Dumping memory at detected peak total memory usage. - LAST = PEAK_MEMORY_USAGE // For IPC macros. -}; - -// Tells the MemoryDumpProvider(s) how much detailed their dumps should be. -// Important: this must be kept consistent with -// services/resource_Coordinator/public/cpp/memory/memory_infra_traits.cc. -enum class MemoryDumpLevelOfDetail : uint32_t { - FIRST, - - // For background tracing mode. The dump time is quick, and typically just the - // totals are expected. Suballocations need not be specified. Dump name must - // contain only pre-defined strings and string arguments cannot be added. - BACKGROUND = FIRST, - - // For the levels below, MemoryDumpProvider instances must guarantee that the - // total size reported in the root node is consistent. Only the granularity of - // the child MemoryAllocatorDump(s) differs with the levels. - - // Few entries, typically a fixed number, per dump. - LIGHT, - - // Unrestricted amount of entries per dump. - DETAILED, - - LAST = DETAILED -}; - -// Initial request arguments for a global memory dump. (see -// MemoryDumpManager::RequestGlobalMemoryDump()). Important: this must be kept -// consistent with services/memory_infra/public/cpp/memory_infra_traits.cc. -struct BASE_EXPORT MemoryDumpRequestArgs { - // Globally unique identifier. In multi-process dumps, all processes issue a - // local dump with the same guid. This allows the trace importers to - // reconstruct the global dump. - uint64_t dump_guid; - - MemoryDumpType dump_type; - MemoryDumpLevelOfDetail level_of_detail; -}; - -// Args for ProcessMemoryDump and passed to OnMemoryDump calls for memory dump -// providers. Dump providers are expected to read the args for creating dumps. -struct MemoryDumpArgs { - // Specifies how detailed the dumps should be. - MemoryDumpLevelOfDetail level_of_detail; -}; - -// TODO(hjd): Not used yet, see crbug.com/703184 -// Summarises information about memory use as seen by a single process. -// This information will eventually be passed to a service to be colated -// and reported. -struct MemoryDumpCallbackResult { - struct OSMemDump { - uint32_t resident_set_kb = 0; - }; - struct ChromeMemDump { - uint32_t malloc_total_kb = 0; - uint32_t partition_alloc_total_kb = 0; - uint32_t blink_gc_total_kb = 0; - uint32_t v8_total_kb = 0; - }; - - // These are for the current process. - OSMemDump os_dump; - ChromeMemDump chrome_dump; - - // In some cases, OS stats can only be dumped from a privileged process to - // get around to sandboxing/selinux restrictions (see crbug.com/461788). - std::map<ProcessId, OSMemDump> extra_processes_dump; - - MemoryDumpCallbackResult(); - ~MemoryDumpCallbackResult(); -}; - -using MemoryDumpCallback = Callback<void(uint64_t dump_guid, bool success)>; - -BASE_EXPORT const char* MemoryDumpTypeToString(const MemoryDumpType& dump_type); - -BASE_EXPORT MemoryDumpType StringToMemoryDumpType(const std::string& str); - -BASE_EXPORT const char* MemoryDumpLevelOfDetailToString( - const MemoryDumpLevelOfDetail& level_of_detail); - -BASE_EXPORT MemoryDumpLevelOfDetail -StringToMemoryDumpLevelOfDetail(const std::string& str); - -} // namespace trace_event -} // namespace base - -#endif // BASE_TRACE_EVENT_MEMORY_DUMP_REQUEST_ARGS_H_ diff --git a/base/trace_event/memory_dump_scheduler.cc b/base/trace_event/memory_dump_scheduler.cc deleted file mode 100644 index 150feb8e79..0000000000 --- a/base/trace_event/memory_dump_scheduler.cc +++ /dev/null @@ -1,328 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/trace_event/memory_dump_scheduler.h" - -#include "base/process/process_metrics.h" -#include "base/single_thread_task_runner.h" -#include "base/threading/thread_task_runner_handle.h" -#include "base/trace_event/memory_dump_manager.h" -#include "build/build_config.h" - -namespace base { -namespace trace_event { - -namespace { -// Threshold on increase in memory from last dump beyond which a new dump must -// be triggered. -int64_t kDefaultMemoryIncreaseThreshold = 50 * 1024 * 1024; // 50MiB -const uint32_t kMemoryTotalsPollingInterval = 25; -uint32_t g_polling_interval_ms_for_testing = 0; -} // namespace - -// static -MemoryDumpScheduler* MemoryDumpScheduler::GetInstance() { - static MemoryDumpScheduler* instance = new MemoryDumpScheduler(); - return instance; -} - -MemoryDumpScheduler::MemoryDumpScheduler() : mdm_(nullptr), is_setup_(false) {} -MemoryDumpScheduler::~MemoryDumpScheduler() {} - -void MemoryDumpScheduler::Setup( - MemoryDumpManager* mdm, - scoped_refptr<SingleThreadTaskRunner> polling_task_runner) { - mdm_ = mdm; - polling_task_runner_ = polling_task_runner; - periodic_state_.reset(new PeriodicTriggerState); - polling_state_.reset(new PollingTriggerState); - is_setup_ = true; -} - -void MemoryDumpScheduler::AddTrigger(MemoryDumpType trigger_type, - MemoryDumpLevelOfDetail level_of_detail, - uint32_t min_time_between_dumps_ms) { - DCHECK(is_setup_); - if (trigger_type == MemoryDumpType::PEAK_MEMORY_USAGE) { - DCHECK(!periodic_state_->is_configured); - DCHECK_EQ(PollingTriggerState::DISABLED, polling_state_->current_state); - DCHECK_NE(0u, min_time_between_dumps_ms); - - polling_state_->level_of_detail = level_of_detail; - polling_state_->min_polls_between_dumps = - (min_time_between_dumps_ms + polling_state_->polling_interval_ms - 1) / - polling_state_->polling_interval_ms; - polling_state_->current_state = PollingTriggerState::CONFIGURED; - } else if (trigger_type == MemoryDumpType::PERIODIC_INTERVAL) { - DCHECK_EQ(PollingTriggerState::DISABLED, polling_state_->current_state); - periodic_state_->is_configured = true; - DCHECK_NE(0u, min_time_between_dumps_ms); - switch (level_of_detail) { - case MemoryDumpLevelOfDetail::BACKGROUND: - break; - case MemoryDumpLevelOfDetail::LIGHT: - DCHECK_EQ(0u, periodic_state_->light_dump_period_ms); - periodic_state_->light_dump_period_ms = min_time_between_dumps_ms; - break; - case MemoryDumpLevelOfDetail::DETAILED: - DCHECK_EQ(0u, periodic_state_->heavy_dump_period_ms); - periodic_state_->heavy_dump_period_ms = min_time_between_dumps_ms; - break; - } - - periodic_state_->min_timer_period_ms = std::min( - periodic_state_->min_timer_period_ms, min_time_between_dumps_ms); - DCHECK_EQ(0u, periodic_state_->light_dump_period_ms % - periodic_state_->min_timer_period_ms); - DCHECK_EQ(0u, periodic_state_->heavy_dump_period_ms % - periodic_state_->min_timer_period_ms); - } -} - -void MemoryDumpScheduler::EnablePeriodicTriggerIfNeeded() { - DCHECK(is_setup_); - if (!periodic_state_->is_configured || periodic_state_->timer.IsRunning()) - return; - periodic_state_->light_dumps_rate = periodic_state_->light_dump_period_ms / - periodic_state_->min_timer_period_ms; - periodic_state_->heavy_dumps_rate = periodic_state_->heavy_dump_period_ms / - periodic_state_->min_timer_period_ms; - - periodic_state_->dump_count = 0; - periodic_state_->timer.Start( - FROM_HERE, - TimeDelta::FromMilliseconds(periodic_state_->min_timer_period_ms), - Bind(&MemoryDumpScheduler::RequestPeriodicGlobalDump, Unretained(this))); -} - -void MemoryDumpScheduler::EnablePollingIfNeeded() { - DCHECK(is_setup_); - if (polling_state_->current_state != PollingTriggerState::CONFIGURED) - return; - - polling_state_->current_state = PollingTriggerState::ENABLED; - polling_state_->ResetTotals(); - - polling_task_runner_->PostTask( - FROM_HERE, - Bind(&MemoryDumpScheduler::PollMemoryOnPollingThread, Unretained(this))); -} - -void MemoryDumpScheduler::NotifyDumpTriggered() { - if (polling_task_runner_ && - !polling_task_runner_->RunsTasksOnCurrentThread()) { - polling_task_runner_->PostTask( - FROM_HERE, - Bind(&MemoryDumpScheduler::NotifyDumpTriggered, Unretained(this))); - return; - } - - if (!polling_state_ || - polling_state_->current_state != PollingTriggerState::ENABLED) { - return; - } - - polling_state_->ResetTotals(); -} - -void MemoryDumpScheduler::DisableAllTriggers() { - if (periodic_state_) { - if (periodic_state_->timer.IsRunning()) - periodic_state_->timer.Stop(); - periodic_state_.reset(); - } - - if (polling_task_runner_) { - DCHECK(polling_state_); - polling_task_runner_->PostTask( - FROM_HERE, Bind(&MemoryDumpScheduler::DisablePollingOnPollingThread, - Unretained(this))); - polling_task_runner_ = nullptr; - } - is_setup_ = false; -} - -void MemoryDumpScheduler::DisablePollingOnPollingThread() { - polling_state_->current_state = PollingTriggerState::DISABLED; - polling_state_.reset(); -} - -// static -void MemoryDumpScheduler::SetPollingIntervalForTesting(uint32_t interval) { - g_polling_interval_ms_for_testing = interval; -} - -bool MemoryDumpScheduler::IsPeriodicTimerRunningForTesting() { - return periodic_state_->timer.IsRunning(); -} - -void MemoryDumpScheduler::RequestPeriodicGlobalDump() { - MemoryDumpLevelOfDetail level_of_detail = MemoryDumpLevelOfDetail::BACKGROUND; - if (periodic_state_->light_dumps_rate > 0 && - periodic_state_->dump_count % periodic_state_->light_dumps_rate == 0) - level_of_detail = MemoryDumpLevelOfDetail::LIGHT; - if (periodic_state_->heavy_dumps_rate > 0 && - periodic_state_->dump_count % periodic_state_->heavy_dumps_rate == 0) - level_of_detail = MemoryDumpLevelOfDetail::DETAILED; - ++periodic_state_->dump_count; - - mdm_->RequestGlobalDump(MemoryDumpType::PERIODIC_INTERVAL, level_of_detail); -} - -void MemoryDumpScheduler::PollMemoryOnPollingThread() { - if (!polling_state_) - return; - - DCHECK_EQ(PollingTriggerState::ENABLED, polling_state_->current_state); - - uint64_t polled_memory = 0; - bool res = mdm_->PollFastMemoryTotal(&polled_memory); - DCHECK(res); - if (polling_state_->level_of_detail == MemoryDumpLevelOfDetail::DETAILED) { - TRACE_COUNTER1(MemoryDumpManager::kTraceCategory, "PolledMemoryMB", - polled_memory / 1024 / 1024); - } - - if (ShouldTriggerDump(polled_memory)) { - TRACE_EVENT_INSTANT1(MemoryDumpManager::kTraceCategory, - "Peak memory dump Triggered", - TRACE_EVENT_SCOPE_PROCESS, "total_usage_MB", - polled_memory / 1024 / 1024); - - mdm_->RequestGlobalDump(MemoryDumpType::PEAK_MEMORY_USAGE, - polling_state_->level_of_detail); - } - - // TODO(ssid): Use RequestSchedulerCallback, crbug.com/607533. - ThreadTaskRunnerHandle::Get()->PostDelayedTask( - FROM_HERE, - Bind(&MemoryDumpScheduler::PollMemoryOnPollingThread, Unretained(this)), - TimeDelta::FromMilliseconds(polling_state_->polling_interval_ms)); -} - -bool MemoryDumpScheduler::ShouldTriggerDump(uint64_t current_memory_total) { - // This function tries to detect peak memory usage as discussed in - // https://goo.gl/0kOU4A. - - if (current_memory_total == 0) - return false; - - bool should_dump = false; - ++polling_state_->num_polls_from_last_dump; - if (polling_state_->last_dump_memory_total == 0) { - // If it's first sample then trigger memory dump. - should_dump = true; - } else if (polling_state_->min_polls_between_dumps > - polling_state_->num_polls_from_last_dump) { - return false; - } - - int64_t increase_from_last_dump = - current_memory_total - polling_state_->last_dump_memory_total; - should_dump |= - increase_from_last_dump > polling_state_->memory_increase_threshold; - should_dump |= IsCurrentSamplePeak(current_memory_total); - if (should_dump) - polling_state_->ResetTotals(); - return should_dump; -} - -bool MemoryDumpScheduler::IsCurrentSamplePeak( - uint64_t current_memory_total_bytes) { - uint64_t current_memory_total_kb = current_memory_total_bytes / 1024; - polling_state_->last_memory_totals_kb_index = - (polling_state_->last_memory_totals_kb_index + 1) % - PollingTriggerState::kMaxNumMemorySamples; - uint64_t mean = 0; - for (uint32_t i = 0; i < PollingTriggerState::kMaxNumMemorySamples; ++i) { - if (polling_state_->last_memory_totals_kb[i] == 0) { - // Not enough samples to detect peaks. - polling_state_ - ->last_memory_totals_kb[polling_state_->last_memory_totals_kb_index] = - current_memory_total_kb; - return false; - } - mean += polling_state_->last_memory_totals_kb[i]; - } - mean = mean / PollingTriggerState::kMaxNumMemorySamples; - uint64_t variance = 0; - for (uint32_t i = 0; i < PollingTriggerState::kMaxNumMemorySamples; ++i) { - variance += (polling_state_->last_memory_totals_kb[i] - mean) * - (polling_state_->last_memory_totals_kb[i] - mean); - } - variance = variance / PollingTriggerState::kMaxNumMemorySamples; - - polling_state_ - ->last_memory_totals_kb[polling_state_->last_memory_totals_kb_index] = - current_memory_total_kb; - - // If stddev is less than 0.2% then we consider that the process is inactive. - bool is_stddev_low = variance < mean / 500 * mean / 500; - if (is_stddev_low) - return false; - - // (mean + 3.69 * stddev) corresponds to a value that is higher than current - // sample with 99.99% probability. - return (current_memory_total_kb - mean) * (current_memory_total_kb - mean) > - (3.69 * 3.69 * variance); -} - -MemoryDumpScheduler::PeriodicTriggerState::PeriodicTriggerState() - : is_configured(false), - dump_count(0), - min_timer_period_ms(std::numeric_limits<uint32_t>::max()), - light_dumps_rate(0), - heavy_dumps_rate(0), - light_dump_period_ms(0), - heavy_dump_period_ms(0) {} - -MemoryDumpScheduler::PeriodicTriggerState::~PeriodicTriggerState() { - DCHECK(!timer.IsRunning()); -} - -MemoryDumpScheduler::PollingTriggerState::PollingTriggerState() - : current_state(DISABLED), - level_of_detail(MemoryDumpLevelOfDetail::FIRST), - polling_interval_ms(g_polling_interval_ms_for_testing - ? g_polling_interval_ms_for_testing - : kMemoryTotalsPollingInterval), - min_polls_between_dumps(0), - num_polls_from_last_dump(-1), - last_dump_memory_total(0), - memory_increase_threshold(0), - last_memory_totals_kb_index(0) {} - -MemoryDumpScheduler::PollingTriggerState::~PollingTriggerState() {} - -void MemoryDumpScheduler::PollingTriggerState::ResetTotals() { - if (!memory_increase_threshold) { - memory_increase_threshold = kDefaultMemoryIncreaseThreshold; -#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX) || \ - defined(OS_ANDROID) - // Set threshold to 1% of total system memory. - SystemMemoryInfoKB meminfo; - bool res = GetSystemMemoryInfo(&meminfo); - if (res) { - memory_increase_threshold = - (static_cast<int64_t>(meminfo.total) / 100) * 1024; - } - DCHECK_GT(memory_increase_threshold, 0u); -#endif - } - - // Update the |last_dump_memory_total|'s value from the totals if it's not - // first poll. - if (num_polls_from_last_dump >= 0 && - last_memory_totals_kb[last_memory_totals_kb_index]) { - last_dump_memory_total = - last_memory_totals_kb[last_memory_totals_kb_index] * 1024; - } - num_polls_from_last_dump = 0; - for (uint32_t i = 0; i < kMaxNumMemorySamples; ++i) - last_memory_totals_kb[i] = 0; - last_memory_totals_kb_index = 0; -} - -} // namespace trace_event -} // namespace base diff --git a/base/trace_event/memory_dump_scheduler.h b/base/trace_event/memory_dump_scheduler.h deleted file mode 100644 index ab8441bc20..0000000000 --- a/base/trace_event/memory_dump_scheduler.h +++ /dev/null @@ -1,163 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_TRACE_EVENT_MEMORY_DUMP_SCHEDULER_H -#define BASE_TRACE_EVENT_MEMORY_DUMP_SCHEDULER_H - -#include <memory> - -#include "base/base_export.h" -#include "base/gtest_prod_util.h" -#include "base/memory/ref_counted.h" -#include "base/timer/timer.h" -#include "base/trace_event/memory_dump_request_args.h" - -namespace base { -class SingleThreadTaskRunner; - -namespace trace_event { - -class MemoryDumpManager; - -// Schedules global dump requests based on the triggers added. The methods of -// this class are NOT thread safe and the client has to take care of invoking -// all the methods of the class safely. -class BASE_EXPORT MemoryDumpScheduler { - public: - static MemoryDumpScheduler* GetInstance(); - - // Initializes the scheduler. NOT thread safe. - void Setup(MemoryDumpManager* mdm_, - scoped_refptr<SingleThreadTaskRunner> polling_task_runner); - - // Adds triggers for scheduling global dumps. Both periodic and peak triggers - // cannot be added together. At the moment the periodic support is limited to - // at most one periodic trigger per dump mode and peak triggers are limited to - // at most one. All intervals should be an integeral multiple of the smallest - // interval specified. NOT thread safe. - void AddTrigger(MemoryDumpType trigger_type, - MemoryDumpLevelOfDetail level_of_detail, - uint32_t min_time_between_dumps_ms); - - // Starts periodic dumps. NOT thread safe and triggers must be added before - // enabling. - void EnablePeriodicTriggerIfNeeded(); - - // Starts polling memory total. NOT thread safe and triggers must be added - // before enabling. - void EnablePollingIfNeeded(); - - // Resets time for triggering dump to account for minimum time between the - // dumps. NOT thread safe. - void NotifyDumpTriggered(); - - // Disables all triggers. NOT thread safe. This should be called before - // polling thread is stopped to stop polling cleanly. - void DisableAllTriggers(); - - private: - friend class MemoryDumpManagerTest; - friend class MemoryDumpSchedulerPollingTest; - FRIEND_TEST_ALL_PREFIXES(MemoryDumpManagerTest, TestPollingOnDumpThread); - FRIEND_TEST_ALL_PREFIXES(MemoryDumpSchedulerPollingTest, NotifyDumpTriggered); - - // Helper class to schdule periodic memory dumps. - struct BASE_EXPORT PeriodicTriggerState { - PeriodicTriggerState(); - ~PeriodicTriggerState(); - - bool is_configured; - - RepeatingTimer timer; - uint32_t dump_count; - uint32_t min_timer_period_ms; - uint32_t light_dumps_rate; - uint32_t heavy_dumps_rate; - - uint32_t light_dump_period_ms; - uint32_t heavy_dump_period_ms; - - DISALLOW_COPY_AND_ASSIGN(PeriodicTriggerState); - }; - - struct BASE_EXPORT PollingTriggerState { - enum State { - CONFIGURED, // Polling trigger was added. - ENABLED, // Polling is running. - DISABLED // Polling is disabled. - }; - - static const uint32_t kMaxNumMemorySamples = 50; - - PollingTriggerState(); - ~PollingTriggerState(); - - // Helper to clear the tracked memory totals and poll count from last dump. - void ResetTotals(); - - State current_state; - MemoryDumpLevelOfDetail level_of_detail; - - uint32_t polling_interval_ms; - - // Minimum numer of polls after the last dump at which next dump can be - // triggered. - int min_polls_between_dumps; - int num_polls_from_last_dump; - - uint64_t last_dump_memory_total; - int64_t memory_increase_threshold; - uint64_t last_memory_totals_kb[kMaxNumMemorySamples]; - uint32_t last_memory_totals_kb_index; - - DISALLOW_COPY_AND_ASSIGN(PollingTriggerState); - }; - - MemoryDumpScheduler(); - ~MemoryDumpScheduler(); - - // Helper to set polling disabled. - void DisablePollingOnPollingThread(); - - // Periodically called by the timer. - void RequestPeriodicGlobalDump(); - - // Called for polling memory usage and trigger dumps if peak is detected. - void PollMemoryOnPollingThread(); - - // Returns true if peak memory value is detected. - bool ShouldTriggerDump(uint64_t current_memory_total); - - // Helper to detect peaks in memory usage. - bool IsCurrentSamplePeak(uint64_t current_memory_total); - - // Must be set before enabling tracing. - static void SetPollingIntervalForTesting(uint32_t interval); - - // True if periodic dumping is enabled. - bool IsPeriodicTimerRunningForTesting(); - - MemoryDumpManager* mdm_; - - // Accessed on the thread of the client before enabling and only accessed on - // the thread that called "EnablePeriodicTriggersIfNeeded()" after enabling. - std::unique_ptr<PeriodicTriggerState> periodic_state_; - - // Accessed on the thread of the client before enabling and only accessed on - // the polling thread after enabling. - std::unique_ptr<PollingTriggerState> polling_state_; - - // Accessed on the thread of the client only. - scoped_refptr<SingleThreadTaskRunner> polling_task_runner_; - - // True when the scheduler is setup. Accessed on the thread of client only. - bool is_setup_; - - DISALLOW_COPY_AND_ASSIGN(MemoryDumpScheduler); -}; - -} // namespace trace_event -} // namespace base - -#endif // BASE_TRACE_EVENT_MEMORY_DUMP_SCHEDULER_H diff --git a/base/trace_event/memory_dump_scheduler_unittest.cc b/base/trace_event/memory_dump_scheduler_unittest.cc deleted file mode 100644 index 9af2a3b430..0000000000 --- a/base/trace_event/memory_dump_scheduler_unittest.cc +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/trace_event/memory_dump_scheduler.h" - -#include <memory> - -#include "base/single_thread_task_runner.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { -namespace trace_event { - -class MemoryDumpSchedulerPollingTest : public testing::Test { - public: - static const uint32_t kMinPollsToDump = 5; - - MemoryDumpSchedulerPollingTest() - : testing::Test(), - num_samples_tracked_( - MemoryDumpScheduler::PollingTriggerState::kMaxNumMemorySamples) {} - - void SetUp() override { - MemoryDumpScheduler::SetPollingIntervalForTesting(1); - uint32_t kMinPollsToDump = 5; - mds_ = MemoryDumpScheduler::GetInstance(); - mds_->Setup(nullptr, nullptr); - mds_->AddTrigger(MemoryDumpType::PEAK_MEMORY_USAGE, - MemoryDumpLevelOfDetail::LIGHT, kMinPollsToDump); - mds_->polling_state_->ResetTotals(); - mds_->polling_state_->current_state = - MemoryDumpScheduler::PollingTriggerState::ENABLED; - } - - void TearDown() override { - mds_->polling_state_->current_state = - MemoryDumpScheduler::PollingTriggerState::DISABLED; - } - - protected: - bool ShouldTriggerDump(uint64_t total) { - return mds_->ShouldTriggerDump(total); - } - - uint32_t num_samples_tracked_; - MemoryDumpScheduler* mds_; -}; - -TEST_F(MemoryDumpSchedulerPollingTest, PeakDetection) { - for (uint32_t i = 0; i < num_samples_tracked_ * 6; ++i) { - // Memory is increased in steps and dumps must be triggered at every step. - uint64_t total = (2 + (i / (2 * num_samples_tracked_))) * 1024 * 1204; - bool did_trigger = ShouldTriggerDump(total); - // Dumps must be triggered only at specific iterations. - bool should_have_triggered = i == 0; - should_have_triggered |= - (i > num_samples_tracked_) && (i % (2 * num_samples_tracked_) == 1); - if (should_have_triggered) { - ASSERT_TRUE(did_trigger) << "Dump wasn't triggered at " << i; - } else { - ASSERT_FALSE(did_trigger) << "Unexpected dump at " << i; - } - } -} - -TEST_F(MemoryDumpSchedulerPollingTest, SlowGrowthDetection) { - for (uint32_t i = 0; i < 15; ++i) { - // Record 1GiB of increase in each call. Dumps are triggered with 1% w.r.t - // system's total memory. - uint64_t total = static_cast<uint64_t>(i + 1) * 1024 * 1024 * 1024; - bool did_trigger = ShouldTriggerDump(total); - bool should_have_triggered = i % kMinPollsToDump == 0; - if (should_have_triggered) { - ASSERT_TRUE(did_trigger) << "Dump wasn't triggered at " << i; - } else { - ASSERT_FALSE(did_trigger) << "Unexpected dump at " << i; - } - } -} - -TEST_F(MemoryDumpSchedulerPollingTest, NotifyDumpTriggered) { - for (uint32_t i = 0; i < num_samples_tracked_ * 6; ++i) { - uint64_t total = (2 + (i / (2 * num_samples_tracked_))) * 1024 * 1204; - if (i % num_samples_tracked_ == 0) - mds_->NotifyDumpTriggered(); - bool did_trigger = ShouldTriggerDump(total); - // Dumps should never be triggered since NotifyDumpTriggered() is called - // frequently. - EXPECT_NE(0u, mds_->polling_state_->last_dump_memory_total); - EXPECT_GT(num_samples_tracked_ - 1, - mds_->polling_state_->last_memory_totals_kb_index); - EXPECT_LT(static_cast<int64_t>( - total - mds_->polling_state_->last_dump_memory_total), - mds_->polling_state_->memory_increase_threshold); - ASSERT_FALSE(did_trigger && i) << "Unexpected dump at " << i; - } -} - -} // namespace trace_event -} // namespace base diff --git a/base/trace_event/memory_dump_session_state.cc b/base/trace_event/memory_dump_session_state.cc deleted file mode 100644 index d26b82a5b7..0000000000 --- a/base/trace_event/memory_dump_session_state.cc +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/trace_event/memory_dump_session_state.h" - -namespace base { -namespace trace_event { - -MemoryDumpSessionState::MemoryDumpSessionState() - : heap_profiler_breakdown_threshold_bytes_(0) {} -MemoryDumpSessionState::~MemoryDumpSessionState() {} - -void MemoryDumpSessionState::SetStackFrameDeduplicator( - std::unique_ptr<StackFrameDeduplicator> stack_frame_deduplicator) { - DCHECK(!stack_frame_deduplicator_); - stack_frame_deduplicator_ = std::move(stack_frame_deduplicator); -} - -void MemoryDumpSessionState::SetTypeNameDeduplicator( - std::unique_ptr<TypeNameDeduplicator> type_name_deduplicator) { - DCHECK(!type_name_deduplicator_); - type_name_deduplicator_ = std::move(type_name_deduplicator); -} - -void MemoryDumpSessionState::SetAllowedDumpModes( - std::set<MemoryDumpLevelOfDetail> allowed_dump_modes) { - allowed_dump_modes_ = allowed_dump_modes; -} - -bool MemoryDumpSessionState::IsDumpModeAllowed( - MemoryDumpLevelOfDetail dump_mode) const { - return allowed_dump_modes_.count(dump_mode) != 0; -} - -} // namespace trace_event -} // namespace base diff --git a/base/trace_event/memory_dump_session_state.h b/base/trace_event/memory_dump_session_state.h deleted file mode 100644 index 46092cb483..0000000000 --- a/base/trace_event/memory_dump_session_state.h +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_TRACE_EVENT_MEMORY_DUMP_SESSION_STATE_H_ -#define BASE_TRACE_EVENT_MEMORY_DUMP_SESSION_STATE_H_ - -#include <memory> -#include <set> - -#include "base/base_export.h" -#include "base/trace_event/heap_profiler_stack_frame_deduplicator.h" -#include "base/trace_event/heap_profiler_type_name_deduplicator.h" -#include "base/trace_event/memory_dump_request_args.h" - -namespace base { -namespace trace_event { - -// Container for state variables that should be shared across all the memory -// dumps in a tracing session. -class BASE_EXPORT MemoryDumpSessionState - : public RefCountedThreadSafe<MemoryDumpSessionState> { - public: - MemoryDumpSessionState(); - - // Returns the stack frame deduplicator that should be used by memory dump - // providers when doing a heap dump. - StackFrameDeduplicator* stack_frame_deduplicator() const { - return stack_frame_deduplicator_.get(); - } - - void SetStackFrameDeduplicator( - std::unique_ptr<StackFrameDeduplicator> stack_frame_deduplicator); - - // Returns the type name deduplicator that should be used by memory dump - // providers when doing a heap dump. - TypeNameDeduplicator* type_name_deduplicator() const { - return type_name_deduplicator_.get(); - } - - void SetTypeNameDeduplicator( - std::unique_ptr<TypeNameDeduplicator> type_name_deduplicator); - - void SetAllowedDumpModes( - std::set<MemoryDumpLevelOfDetail> allowed_dump_modes); - - bool IsDumpModeAllowed(MemoryDumpLevelOfDetail dump_mode) const; - - void set_heap_profiler_breakdown_threshold_bytes(uint32_t value) { - heap_profiler_breakdown_threshold_bytes_ = value; - } - - uint32_t heap_profiler_breakdown_threshold_bytes() const { - return heap_profiler_breakdown_threshold_bytes_; - } - - private: - friend class RefCountedThreadSafe<MemoryDumpSessionState>; - ~MemoryDumpSessionState(); - - // Deduplicates backtraces in heap dumps so they can be written once when the - // trace is finalized. - std::unique_ptr<StackFrameDeduplicator> stack_frame_deduplicator_; - - // Deduplicates type names in heap dumps so they can be written once when the - // trace is finalized. - std::unique_ptr<TypeNameDeduplicator> type_name_deduplicator_; - - std::set<MemoryDumpLevelOfDetail> allowed_dump_modes_; - - uint32_t heap_profiler_breakdown_threshold_bytes_; -}; - -} // namespace trace_event -} // namespace base - -#endif // BASE_TRACE_EVENT_MEMORY_DUMP_SESSION_STATE_H_ diff --git a/base/trace_event/memory_infra_background_whitelist.cc b/base/trace_event/memory_infra_background_whitelist.cc deleted file mode 100644 index 746068a7b1..0000000000 --- a/base/trace_event/memory_infra_background_whitelist.cc +++ /dev/null @@ -1,258 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/trace_event/memory_infra_background_whitelist.h" - -#include <ctype.h> -#include <string.h> - -#include <string> - -namespace base { -namespace trace_event { -namespace { - -// The names of dump providers whitelisted for background tracing. Dump -// providers can be added here only if the background mode dump has very -// less performance and memory overhead. -const char* const kDumpProviderWhitelist[] = { - "android::ResourceManagerImpl", - "BlinkGC", - "ClientDiscardableSharedMemoryManager", - "DOMStorage", - "DiscardableSharedMemoryManager", - "IndexedDBBackingStore", - "JavaHeap", - "LevelDB", - "LeveldbValueStore", - "Malloc", - "MemoryCache", - "PartitionAlloc", - "ProcessMemoryMetrics", - "Skia", - "Sql", - "URLRequestContext", - "V8Isolate", - "WinHeap", - "SyncDirectory", - "TabRestoreServiceHelper", - nullptr // End of list marker. -}; - -// A list of string names that are allowed for the memory allocator dumps in -// background mode. -const char* const kAllocatorDumpNameWhitelist[] = { - "blink_gc", - "blink_gc/allocated_objects", - "discardable", - "discardable/child_0x?", - "dom_storage/0x?/cache_size", - "dom_storage/session_storage_0x?", - "java_heap", - "java_heap/allocated_objects", - "leveldb/index_db/0x?", - "leveldb/leveldb_proto/0x?", - "leveldb/value_store/Extensions.Database.Open.Settings/0x?", - "leveldb/value_store/Extensions.Database.Open.Rules/0x?", - "leveldb/value_store/Extensions.Database.Open.State/0x?", - "leveldb/value_store/Extensions.Database.Open/0x?", - "leveldb/value_store/Extensions.Database.Restore/0x?", - "leveldb/value_store/Extensions.Database.Value.Restore/0x?", - "malloc", - "malloc/allocated_objects", - "malloc/metadata_fragmentation_caches", - "net/http_network_session_0x?", - "net/http_network_session_0x?/quic_stream_factory", - "net/http_network_session_0x?/socket_pool", - "net/http_network_session_0x?/spdy_session_pool", - "net/http_network_session_0x?/stream_factory", - "net/sdch_manager_0x?", - "net/ssl_session_cache", - "net/url_request_context", - "net/url_request_context/app_request", - "net/url_request_context/app_request/0x?", - "net/url_request_context/app_request/0x?/http_cache", - "net/url_request_context/app_request/0x?/http_cache/memory_backend", - "net/url_request_context/app_request/0x?/http_cache/simple_backend", - "net/url_request_context/app_request/0x?/http_network_session", - "net/url_request_context/app_request/0x?/sdch_manager", - "net/url_request_context/extensions", - "net/url_request_context/extensions/0x?", - "net/url_request_context/extensions/0x?/http_cache", - "net/url_request_context/extensions/0x?/http_cache/memory_backend", - "net/url_request_context/extensions/0x?/http_cache/simple_backend", - "net/url_request_context/extensions/0x?/http_network_session", - "net/url_request_context/extensions/0x?/sdch_manager", - "net/url_request_context/isolated_media", - "net/url_request_context/isolated_media/0x?", - "net/url_request_context/isolated_media/0x?/http_cache", - "net/url_request_context/isolated_media/0x?/http_cache/memory_backend", - "net/url_request_context/isolated_media/0x?/http_cache/simple_backend", - "net/url_request_context/isolated_media/0x?/http_network_session", - "net/url_request_context/isolated_media/0x?/sdch_manager", - "net/url_request_context/main", - "net/url_request_context/main/0x?", - "net/url_request_context/main/0x?/http_cache", - "net/url_request_context/main/0x?/http_cache/memory_backend", - "net/url_request_context/main/0x?/http_cache/simple_backend", - "net/url_request_context/main/0x?/http_network_session", - "net/url_request_context/main/0x?/sdch_manager", - "net/url_request_context/main_media", - "net/url_request_context/main_media/0x?", - "net/url_request_context/main_media/0x?/http_cache", - "net/url_request_context/main_media/0x?/http_cache/memory_backend", - "net/url_request_context/main_media/0x?/http_cache/simple_backend", - "net/url_request_context/main_media/0x?/http_network_session", - "net/url_request_context/main_media/0x?/sdch_manager", - "net/url_request_context/proxy", - "net/url_request_context/proxy/0x?", - "net/url_request_context/proxy/0x?/http_cache", - "net/url_request_context/proxy/0x?/http_cache/memory_backend", - "net/url_request_context/proxy/0x?/http_cache/simple_backend", - "net/url_request_context/proxy/0x?/http_network_session", - "net/url_request_context/proxy/0x?/sdch_manager", - "net/url_request_context/safe_browsing", - "net/url_request_context/safe_browsing/0x?", - "net/url_request_context/safe_browsing/0x?/http_cache", - "net/url_request_context/safe_browsing/0x?/http_cache/memory_backend", - "net/url_request_context/safe_browsing/0x?/http_cache/simple_backend", - "net/url_request_context/safe_browsing/0x?/http_network_session", - "net/url_request_context/safe_browsing/0x?/sdch_manager", - "net/url_request_context/system", - "net/url_request_context/system/0x?", - "net/url_request_context/system/0x?/http_cache", - "net/url_request_context/system/0x?/http_cache/memory_backend", - "net/url_request_context/system/0x?/http_cache/simple_backend", - "net/url_request_context/system/0x?/http_network_session", - "net/url_request_context/system/0x?/sdch_manager", - "net/url_request_context/unknown", - "net/url_request_context/unknown/0x?", - "net/url_request_context/unknown/0x?/http_cache", - "net/url_request_context/unknown/0x?/http_cache/memory_backend", - "net/url_request_context/unknown/0x?/http_cache/simple_backend", - "net/url_request_context/unknown/0x?/http_network_session", - "net/url_request_context/unknown/0x?/sdch_manager", - "web_cache/Image_resources", - "web_cache/CSS stylesheet_resources", - "web_cache/Script_resources", - "web_cache/XSL stylesheet_resources", - "web_cache/Font_resources", - "web_cache/Other_resources", - "partition_alloc/allocated_objects", - "partition_alloc/partitions", - "partition_alloc/partitions/array_buffer", - "partition_alloc/partitions/buffer", - "partition_alloc/partitions/fast_malloc", - "partition_alloc/partitions/layout", - "skia/sk_glyph_cache", - "skia/sk_resource_cache", - "sqlite", - "ui/resource_manager_0x?", - "v8/isolate_0x?/heap_spaces", - "v8/isolate_0x?/heap_spaces/code_space", - "v8/isolate_0x?/heap_spaces/large_object_space", - "v8/isolate_0x?/heap_spaces/map_space", - "v8/isolate_0x?/heap_spaces/new_space", - "v8/isolate_0x?/heap_spaces/old_space", - "v8/isolate_0x?/heap_spaces/other_spaces", - "v8/isolate_0x?/malloc", - "v8/isolate_0x?/zapped_for_debug", - "winheap", - "winheap/allocated_objects", - "sync/0x?/kernel", - "sync/0x?/store", - "sync/0x?/model_type/APP", - "sync/0x?/model_type/APP_LIST", - "sync/0x?/model_type/APP_NOTIFICATION", - "sync/0x?/model_type/APP_SETTING", - "sync/0x?/model_type/ARC_PACKAGE", - "sync/0x?/model_type/ARTICLE", - "sync/0x?/model_type/AUTOFILL", - "sync/0x?/model_type/AUTOFILL_PROFILE", - "sync/0x?/model_type/AUTOFILL_WALLET", - "sync/0x?/model_type/BOOKMARK", - "sync/0x?/model_type/DEVICE_INFO", - "sync/0x?/model_type/DICTIONARY", - "sync/0x?/model_type/EXPERIMENTS", - "sync/0x?/model_type/EXTENSION", - "sync/0x?/model_type/EXTENSION_SETTING", - "sync/0x?/model_type/FAVICON_IMAGE", - "sync/0x?/model_type/FAVICON_TRACKING", - "sync/0x?/model_type/HISTORY_DELETE_DIRECTIVE", - "sync/0x?/model_type/MANAGED_USER", - "sync/0x?/model_type/MANAGED_USER_SETTING", - "sync/0x?/model_type/MANAGED_USER_SHARED_SETTING", - "sync/0x?/model_type/MANAGED_USER_WHITELIST", - "sync/0x?/model_type/NIGORI", - "sync/0x?/model_type/PASSWORD", - "sync/0x?/model_type/PREFERENCE", - "sync/0x?/model_type/PRINTER", - "sync/0x?/model_type/PRIORITY_PREFERENCE", - "sync/0x?/model_type/READING_LIST", - "sync/0x?/model_type/SEARCH_ENGINE", - "sync/0x?/model_type/SESSION", - "sync/0x?/model_type/SYNCED_NOTIFICATION", - "sync/0x?/model_type/SYNCED_NOTIFICATION_APP_INFO", - "sync/0x?/model_type/THEME", - "sync/0x?/model_type/TYPED_URL", - "sync/0x?/model_type/WALLET_METADATA", - "sync/0x?/model_type/WIFI_CREDENTIAL", - "tab_restore/service_helper_0x?/entries", - "tab_restore/service_helper_0x?/entries/tab_0x?", - "tab_restore/service_helper_0x?/entries/window_0x?", - nullptr // End of list marker. -}; - -const char* const* g_dump_provider_whitelist = kDumpProviderWhitelist; -const char* const* g_allocator_dump_name_whitelist = - kAllocatorDumpNameWhitelist; - -} // namespace - -bool IsMemoryDumpProviderWhitelisted(const char* mdp_name) { - for (size_t i = 0; g_dump_provider_whitelist[i] != nullptr; ++i) { - if (strcmp(mdp_name, g_dump_provider_whitelist[i]) == 0) - return true; - } - return false; -} - -bool IsMemoryAllocatorDumpNameWhitelisted(const std::string& name) { - // Remove special characters, numbers (including hexadecimal which are marked - // by '0x') from the given string. - const size_t length = name.size(); - std::string stripped_str; - stripped_str.reserve(length); - bool parsing_hex = false; - for (size_t i = 0; i < length; ++i) { - if (parsing_hex && isxdigit(name[i])) - continue; - parsing_hex = false; - if (i + 1 < length && name[i] == '0' && name[i + 1] == 'x') { - parsing_hex = true; - stripped_str.append("0x?"); - ++i; - } else { - stripped_str.push_back(name[i]); - } - } - - for (size_t i = 0; g_allocator_dump_name_whitelist[i] != nullptr; ++i) { - if (stripped_str == g_allocator_dump_name_whitelist[i]) { - return true; - } - } - return false; -} - -void SetDumpProviderWhitelistForTesting(const char* const* list) { - g_dump_provider_whitelist = list; -} - -void SetAllocatorDumpNameWhitelistForTesting(const char* const* list) { - g_allocator_dump_name_whitelist = list; -} - -} // namespace trace_event -} // namespace base diff --git a/base/trace_event/memory_infra_background_whitelist.h b/base/trace_event/memory_infra_background_whitelist.h deleted file mode 100644 index b8d704ae24..0000000000 --- a/base/trace_event/memory_infra_background_whitelist.h +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_TRACE_EVENT_MEMORY_INFRA_BACKGROUND_WHITELIST_H_ -#define BASE_TRACE_EVENT_MEMORY_INFRA_BACKGROUND_WHITELIST_H_ - -// This file contains the whitelists for background mode to limit the tracing -// overhead and remove sensitive information from traces. - -#include <string> - -#include "base/base_export.h" - -namespace base { -namespace trace_event { - -// Checks if the given |mdp_name| is in the whitelist. -bool BASE_EXPORT IsMemoryDumpProviderWhitelisted(const char* mdp_name); - -// Checks if the given |name| matches any of the whitelisted patterns. -bool BASE_EXPORT IsMemoryAllocatorDumpNameWhitelisted(const std::string& name); - -// The whitelist is replaced with the given list for tests. The last element of -// the list must be nullptr. -void BASE_EXPORT SetDumpProviderWhitelistForTesting(const char* const* list); -void BASE_EXPORT -SetAllocatorDumpNameWhitelistForTesting(const char* const* list); - -} // namespace trace_event -} // namespace base - -#endif // BASE_TRACE_EVENT_MEMORY_INFRA_BACKGROUND_WHITELIST_H_ diff --git a/base/trace_event/memory_usage_estimator.cc b/base/trace_event/memory_usage_estimator.cc deleted file mode 100644 index c769d5b6f1..0000000000 --- a/base/trace_event/memory_usage_estimator.cc +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/trace_event/memory_usage_estimator.h" - -namespace base { -namespace trace_event { - -template size_t EstimateMemoryUsage(const std::string&); -template size_t EstimateMemoryUsage(const string16&); - -} // namespace trace_event -} // namespace base diff --git a/base/trace_event/memory_usage_estimator.h b/base/trace_event/memory_usage_estimator.h deleted file mode 100644 index 6f02bb93bb..0000000000 --- a/base/trace_event/memory_usage_estimator.h +++ /dev/null @@ -1,549 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_TRACE_EVENT_MEMORY_USAGE_ESTIMATOR_H_ -#define BASE_TRACE_EVENT_MEMORY_USAGE_ESTIMATOR_H_ - -#include <stdint.h> - -#include <array> -#include <deque> -#include <list> -#include <map> -#include <memory> -#include <queue> -#include <set> -#include <stack> -#include <string> -#include <type_traits> -#include <unordered_map> -#include <unordered_set> -#include <vector> - -#include "base/base_export.h" -#include "base/containers/linked_list.h" -#include "base/strings/string16.h" -#include "base/template_util.h" - -// Composable memory usage estimators. -// -// This file defines set of EstimateMemoryUsage(object) functions that return -// approximate memory usage of their argument. -// -// The ultimate goal is to make memory usage estimation for a class simply a -// matter of aggregating EstimateMemoryUsage() results over all fields. -// -// That is achieved via composability: if EstimateMemoryUsage() is defined -// for T then EstimateMemoryUsage() is also defined for any combination of -// containers holding T (e.g. std::map<int, std::vector<T>>). -// -// There are two ways of defining EstimateMemoryUsage() for a type: -// -// 1. As a global function 'size_t EstimateMemoryUsage(T)' in -// in base::trace_event namespace. -// -// 2. As 'size_t T::EstimateMemoryUsage() const' method. In this case -// EstimateMemoryUsage(T) function in base::trace_event namespace is -// provided automatically. -// -// Here is an example implementation: -// -// size_t foo::bar::MyClass::EstimateMemoryUsage() const { -// return base::trace_event::EstimateMemoryUsage(name_) + -// base::trace_event::EstimateMemoryUsage(id_) + -// base::trace_event::EstimateMemoryUsage(items_); -// } -// -// The approach is simple: first call EstimateMemoryUsage() on all members, -// then recursively fix compilation errors that are caused by types not -// implementing EstimateMemoryUsage(). - -namespace base { -namespace trace_event { - -// Declarations - -// If T declares 'EstimateMemoryUsage() const' member function, then -// global function EstimateMemoryUsage(T) is available, and just calls -// the member function. -template <class T> -auto EstimateMemoryUsage(const T& object) - -> decltype(object.EstimateMemoryUsage()); - -// String - -template <class C, class T, class A> -size_t EstimateMemoryUsage(const std::basic_string<C, T, A>& string); - -// Arrays - -template <class T, size_t N> -size_t EstimateMemoryUsage(const std::array<T, N>& array); - -template <class T, size_t N> -size_t EstimateMemoryUsage(T (&array)[N]); - -template <class T> -size_t EstimateMemoryUsage(const T* array, size_t array_length); - -// std::unique_ptr - -template <class T, class D> -size_t EstimateMemoryUsage(const std::unique_ptr<T, D>& ptr); - -template <class T, class D> -size_t EstimateMemoryUsage(const std::unique_ptr<T[], D>& array, - size_t array_length); - -// std::shared_ptr - -template <class T> -size_t EstimateMemoryUsage(const std::shared_ptr<T>& ptr); - -// Containers - -template <class F, class S> -size_t EstimateMemoryUsage(const std::pair<F, S>& pair); - -template <class T, class A> -size_t EstimateMemoryUsage(const std::vector<T, A>& vector); - -template <class T, class A> -size_t EstimateMemoryUsage(const std::list<T, A>& list); - -template <class T> -size_t EstimateMemoryUsage(const base::LinkedList<T>& list); - -template <class T, class C, class A> -size_t EstimateMemoryUsage(const std::set<T, C, A>& set); - -template <class T, class C, class A> -size_t EstimateMemoryUsage(const std::multiset<T, C, A>& set); - -template <class K, class V, class C, class A> -size_t EstimateMemoryUsage(const std::map<K, V, C, A>& map); - -template <class K, class V, class C, class A> -size_t EstimateMemoryUsage(const std::multimap<K, V, C, A>& map); - -template <class T, class H, class KE, class A> -size_t EstimateMemoryUsage(const std::unordered_set<T, H, KE, A>& set); - -template <class T, class H, class KE, class A> -size_t EstimateMemoryUsage(const std::unordered_multiset<T, H, KE, A>& set); - -template <class K, class V, class H, class KE, class A> -size_t EstimateMemoryUsage(const std::unordered_map<K, V, H, KE, A>& map); - -template <class K, class V, class H, class KE, class A> -size_t EstimateMemoryUsage(const std::unordered_multimap<K, V, H, KE, A>& map); - -template <class T, class A> -size_t EstimateMemoryUsage(const std::deque<T, A>& deque); - -template <class T, class C> -size_t EstimateMemoryUsage(const std::queue<T, C>& queue); - -template <class T, class C> -size_t EstimateMemoryUsage(const std::priority_queue<T, C>& queue); - -template <class T, class C> -size_t EstimateMemoryUsage(const std::stack<T, C>& stack); - -// TODO(dskiba): -// std::forward_list - -// Definitions - -namespace internal { - -// HasEMU<T>::value is true iff EstimateMemoryUsage(T) is available. -// (This is the default version, which is false.) -template <class T, class X = void> -struct HasEMU : std::false_type {}; - -// This HasEMU specialization is only picked up if there exists function -// EstimateMemoryUsage(const T&) that returns size_t. Simpler ways to -// achieve this don't work on MSVC. -template <class T> -struct HasEMU< - T, - typename std::enable_if<std::is_same< - size_t, - decltype(EstimateMemoryUsage(std::declval<const T&>()))>::value>::type> - : std::true_type {}; - -// EMUCaller<T> does three things: -// 1. Defines Call() method that calls EstimateMemoryUsage(T) if it's -// available. -// 2. If EstimateMemoryUsage(T) is not available, but T has trivial dtor -// (i.e. it's POD, integer, pointer, enum, etc.) then it defines Call() -// method that returns 0. This is useful for containers, which allocate -// memory regardless of T (also for cases like std::map<int, MyClass>). -// 3. Finally, if EstimateMemoryUsage(T) is not available, then it triggers -// a static_assert with a helpful message. That cuts numbers of errors -// considerably - if you just call EstimateMemoryUsage(T) but it's not -// available for T, then compiler will helpfully list *all* possible -// variants of it, with an explanation for each. -template <class T, class X = void> -struct EMUCaller { - // std::is_same<> below makes static_assert depend on T, in order to - // prevent it from asserting regardless instantiation. - static_assert(std::is_same<T, std::false_type>::value, - "Neither global function 'size_t EstimateMemoryUsage(T)' " - "nor member function 'size_t T::EstimateMemoryUsage() const' " - "is defined for the type."); - - static size_t Call(const T&) { return 0; } -}; - -template <class T> -struct EMUCaller<T, typename std::enable_if<HasEMU<T>::value>::type> { - static size_t Call(const T& value) { return EstimateMemoryUsage(value); } -}; - -template <class T> -struct EMUCaller< - T, - typename std::enable_if<!HasEMU<T>::value && - is_trivially_destructible<T>::value>::type> { - static size_t Call(const T& value) { return 0; } -}; - -// Returns reference to the underlying container of a container adapter. -// Works for std::stack, std::queue and std::priority_queue. -template <class A> -const typename A::container_type& GetUnderlyingContainer(const A& adapter) { - struct ExposedAdapter : A { - using A::c; - }; - return adapter.*&ExposedAdapter::c; -} - -} // namespace internal - -// Proxy that deducts T and calls EMUCaller<T>. -// To be used by EstimateMemoryUsage() implementations for containers. -template <class T> -size_t EstimateItemMemoryUsage(const T& value) { - return internal::EMUCaller<T>::Call(value); -} - -template <class I> -size_t EstimateIterableMemoryUsage(const I& iterable) { - size_t memory_usage = 0; - for (const auto& item : iterable) { - memory_usage += EstimateItemMemoryUsage(item); - } - return memory_usage; -} - -// Global EstimateMemoryUsage(T) that just calls T::EstimateMemoryUsage(). -template <class T> -auto EstimateMemoryUsage(const T& object) - -> decltype(object.EstimateMemoryUsage()) { - static_assert( - std::is_same<decltype(object.EstimateMemoryUsage()), size_t>::value, - "'T::EstimateMemoryUsage() const' must return size_t."); - return object.EstimateMemoryUsage(); -} - -// String - -template <class C, class T, class A> -size_t EstimateMemoryUsage(const std::basic_string<C, T, A>& string) { - using string_type = std::basic_string<C, T, A>; - using value_type = typename string_type::value_type; - // C++11 doesn't leave much room for implementors - std::string can - // use short string optimization, but that's about it. We detect SSO - // by checking that c_str() points inside |string|. - const uint8_t* cstr = reinterpret_cast<const uint8_t*>(string.c_str()); - const uint8_t* inline_cstr = reinterpret_cast<const uint8_t*>(&string); - if (cstr >= inline_cstr && cstr < inline_cstr + sizeof(string)) { - // SSO string - return 0; - } - return (string.capacity() + 1) * sizeof(value_type); -} - -// Use explicit instantiations from the .cc file (reduces bloat). -extern template BASE_EXPORT size_t EstimateMemoryUsage(const std::string&); -extern template BASE_EXPORT size_t EstimateMemoryUsage(const string16&); - -// Arrays - -template <class T, size_t N> -size_t EstimateMemoryUsage(const std::array<T, N>& array) { - return EstimateIterableMemoryUsage(array); -} - -template <class T, size_t N> -size_t EstimateMemoryUsage(T (&array)[N]) { - return EstimateIterableMemoryUsage(array); -} - -template <class T> -size_t EstimateMemoryUsage(const T* array, size_t array_length) { - size_t memory_usage = sizeof(T) * array_length; - for (size_t i = 0; i != array_length; ++i) { - memory_usage += EstimateItemMemoryUsage(array[i]); - } - return memory_usage; -} - -// std::unique_ptr - -template <class T, class D> -size_t EstimateMemoryUsage(const std::unique_ptr<T, D>& ptr) { - return ptr ? (sizeof(T) + EstimateItemMemoryUsage(*ptr)) : 0; -} - -template <class T, class D> -size_t EstimateMemoryUsage(const std::unique_ptr<T[], D>& array, - size_t array_length) { - return EstimateMemoryUsage(array.get(), array_length); -} - -// std::shared_ptr - -template <class T> -size_t EstimateMemoryUsage(const std::shared_ptr<T>& ptr) { - auto use_count = ptr.use_count(); - if (use_count == 0) { - return 0; - } - // Model shared_ptr after libc++, - // see __shared_ptr_pointer from include/memory - struct SharedPointer { - void* vtbl; - long shared_owners; - long shared_weak_owners; - T* value; - }; - // If object of size S shared N > S times we prefer to (potentially) - // overestimate than to return 0. - return sizeof(SharedPointer) + - (EstimateItemMemoryUsage(*ptr) + (use_count - 1)) / use_count; -} - -// std::pair - -template <class F, class S> -size_t EstimateMemoryUsage(const std::pair<F, S>& pair) { - return EstimateItemMemoryUsage(pair.first) + - EstimateItemMemoryUsage(pair.second); -} - -// std::vector - -template <class T, class A> -size_t EstimateMemoryUsage(const std::vector<T, A>& vector) { - return sizeof(T) * vector.capacity() + EstimateIterableMemoryUsage(vector); -} - -// std::list - -template <class T, class A> -size_t EstimateMemoryUsage(const std::list<T, A>& list) { - using value_type = typename std::list<T, A>::value_type; - struct Node { - Node* prev; - Node* next; - value_type value; - }; - return sizeof(Node) * list.size() + - EstimateIterableMemoryUsage(list); -} - -template <class T> -size_t EstimateMemoryUsage(const base::LinkedList<T>& list) { - size_t memory_usage = 0u; - for (base::LinkNode<T>* node = list.head(); node != list.end(); - node = node->next()) { - // Since we increment by calling node = node->next() we know that node - // isn't nullptr. - memory_usage += EstimateMemoryUsage(*node->value()) + sizeof(T); - } - return memory_usage; -} - -// Tree containers - -template <class V> -size_t EstimateTreeMemoryUsage(size_t size) { - // Tree containers are modeled after libc++ - // (__tree_node from include/__tree) - struct Node { - Node* left; - Node* right; - Node* parent; - bool is_black; - V value; - }; - return sizeof(Node) * size; -} - -template <class T, class C, class A> -size_t EstimateMemoryUsage(const std::set<T, C, A>& set) { - using value_type = typename std::set<T, C, A>::value_type; - return EstimateTreeMemoryUsage<value_type>(set.size()) + - EstimateIterableMemoryUsage(set); -} - -template <class T, class C, class A> -size_t EstimateMemoryUsage(const std::multiset<T, C, A>& set) { - using value_type = typename std::multiset<T, C, A>::value_type; - return EstimateTreeMemoryUsage<value_type>(set.size()) + - EstimateIterableMemoryUsage(set); -} - -template <class K, class V, class C, class A> -size_t EstimateMemoryUsage(const std::map<K, V, C, A>& map) { - using value_type = typename std::map<K, V, C, A>::value_type; - return EstimateTreeMemoryUsage<value_type>(map.size()) + - EstimateIterableMemoryUsage(map); -} - -template <class K, class V, class C, class A> -size_t EstimateMemoryUsage(const std::multimap<K, V, C, A>& map) { - using value_type = typename std::multimap<K, V, C, A>::value_type; - return EstimateTreeMemoryUsage<value_type>(map.size()) + - EstimateIterableMemoryUsage(map); -} - -// HashMap containers - -namespace internal { - -// While hashtable containers model doesn't depend on STL implementation, one -// detail still crept in: bucket_count. It's used in size estimation, but its -// value after inserting N items is not predictable. -// This function is specialized by unittests to return constant value, thus -// excluding bucket_count from testing. -template <class V> -size_t HashMapBucketCountForTesting(size_t bucket_count) { - return bucket_count; -} - -} // namespace internal - -template <class V> -size_t EstimateHashMapMemoryUsage(size_t bucket_count, size_t size) { - // Hashtable containers are modeled after libc++ - // (__hash_node from include/__hash_table) - struct Node { - void* next; - size_t hash; - V value; - }; - using Bucket = void*; - bucket_count = internal::HashMapBucketCountForTesting<V>(bucket_count); - return sizeof(Bucket) * bucket_count + sizeof(Node) * size; -} - -template <class K, class H, class KE, class A> -size_t EstimateMemoryUsage(const std::unordered_set<K, H, KE, A>& set) { - using value_type = typename std::unordered_set<K, H, KE, A>::value_type; - return EstimateHashMapMemoryUsage<value_type>(set.bucket_count(), - set.size()) + - EstimateIterableMemoryUsage(set); -} - -template <class K, class H, class KE, class A> -size_t EstimateMemoryUsage(const std::unordered_multiset<K, H, KE, A>& set) { - using value_type = typename std::unordered_multiset<K, H, KE, A>::value_type; - return EstimateHashMapMemoryUsage<value_type>(set.bucket_count(), - set.size()) + - EstimateIterableMemoryUsage(set); -} - -template <class K, class V, class H, class KE, class A> -size_t EstimateMemoryUsage(const std::unordered_map<K, V, H, KE, A>& map) { - using value_type = typename std::unordered_map<K, V, H, KE, A>::value_type; - return EstimateHashMapMemoryUsage<value_type>(map.bucket_count(), - map.size()) + - EstimateIterableMemoryUsage(map); -} - -template <class K, class V, class H, class KE, class A> -size_t EstimateMemoryUsage(const std::unordered_multimap<K, V, H, KE, A>& map) { - using value_type = - typename std::unordered_multimap<K, V, H, KE, A>::value_type; - return EstimateHashMapMemoryUsage<value_type>(map.bucket_count(), - map.size()) + - EstimateIterableMemoryUsage(map); -} - -// std::deque - -template <class T, class A> -size_t EstimateMemoryUsage(const std::deque<T, A>& deque) { -// Since std::deque implementations are wildly different -// (see crbug.com/674287), we can't have one "good enough" -// way to estimate. - -// kBlockSize - minimum size of a block, in bytes -// kMinBlockLength - number of elements in a block -// if sizeof(T) > kBlockSize -#if defined(_LIBCPP_VERSION) - size_t kBlockSize = 4096; - size_t kMinBlockLength = 16; -#elif defined(__GLIBCXX__) - size_t kBlockSize = 512; - size_t kMinBlockLength = 1; -#elif defined(_MSC_VER) - size_t kBlockSize = 16; - size_t kMinBlockLength = 1; -#else - size_t kBlockSize = 0; - size_t kMinBlockLength = 1; -#endif - - size_t block_length = - (sizeof(T) > kBlockSize) ? kMinBlockLength : kBlockSize / sizeof(T); - - size_t blocks = (deque.size() + block_length - 1) / block_length; - -#if defined(__GLIBCXX__) - // libstdc++: deque always has at least one block - if (!blocks) - blocks = 1; -#endif - -#if defined(_LIBCPP_VERSION) - // libc++: deque keeps at most two blocks when it shrinks, - // so even if the size is zero, deque might be holding up - // to 4096 * 2 bytes. One way to know whether deque has - // ever allocated (and hence has 1 or 2 blocks) is to check - // iterator's pointer. Non-zero value means that deque has - // at least one block. - if (!blocks && deque.begin().operator->()) - blocks = 1; -#endif - - return (blocks * block_length * sizeof(T)) + - EstimateIterableMemoryUsage(deque); -} - -// Container adapters - -template <class T, class C> -size_t EstimateMemoryUsage(const std::queue<T, C>& queue) { - return EstimateMemoryUsage(internal::GetUnderlyingContainer(queue)); -} - -template <class T, class C> -size_t EstimateMemoryUsage(const std::priority_queue<T, C>& queue) { - return EstimateMemoryUsage(internal::GetUnderlyingContainer(queue)); -} - -template <class T, class C> -size_t EstimateMemoryUsage(const std::stack<T, C>& stack) { - return EstimateMemoryUsage(internal::GetUnderlyingContainer(stack)); -} - -} // namespace trace_event -} // namespace base - -#endif // BASE_TRACE_EVENT_MEMORY_USAGE_ESTIMATOR_H_ diff --git a/base/trace_event/memory_usage_estimator_unittest.cc b/base/trace_event/memory_usage_estimator_unittest.cc deleted file mode 100644 index 80237c0192..0000000000 --- a/base/trace_event/memory_usage_estimator_unittest.cc +++ /dev/null @@ -1,244 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/trace_event/memory_usage_estimator.h" - -#include <stdlib.h> - -#include "base/memory/ptr_util.h" -#include "base/strings/string16.h" -#include "build/build_config.h" -#include "testing/gtest/include/gtest/gtest.h" - -#if defined(ARCH_CPU_64_BITS) -#define EXPECT_EQ_32_64(_, e, a) EXPECT_EQ(e, a) -#else -#define EXPECT_EQ_32_64(e, _, a) EXPECT_EQ(e, a) -#endif - -namespace base { -namespace trace_event { - -namespace { - -// Test class with predictable memory usage. -class Data { - public: - explicit Data(size_t size = 17): size_(size) { - } - - size_t size() const { return size_; } - - size_t EstimateMemoryUsage() const { - return size_; - } - - bool operator < (const Data& other) const { - return size_ < other.size_; - } - bool operator == (const Data& other) const { - return size_ == other.size_; - } - - struct Hasher { - size_t operator () (const Data& data) const { - return data.size(); - } - }; - - private: - size_t size_; -}; - -} // namespace - -namespace internal { - -// This kills variance of bucket_count across STL implementations. -template <> -size_t HashMapBucketCountForTesting<Data>(size_t) { - return 10; -} -template <> -size_t HashMapBucketCountForTesting<std::pair<const Data, short>>(size_t) { - return 10; -} - -} // namespace internal - -TEST(EstimateMemoryUsageTest, String) { - std::string string(777, 'a'); - EXPECT_EQ(string.capacity() + 1, EstimateMemoryUsage(string)); -} - -TEST(EstimateMemoryUsageTest, String16) { - string16 string(777, 'a'); - EXPECT_EQ(sizeof(char16) * (string.capacity() + 1), - EstimateMemoryUsage(string)); -} - -TEST(EstimateMemoryUsageTest, Arrays) { - // std::array - { - std::array<Data, 10> array; - EXPECT_EQ(170u, EstimateMemoryUsage(array)); - } - - // T[N] - { - Data array[10]; - EXPECT_EQ(170u, EstimateMemoryUsage(array)); - } - - // C array - { - struct Item { - char payload[10]; - }; - Item* array = new Item[7]; - EXPECT_EQ(70u, EstimateMemoryUsage(array, 7)); - delete[] array; - } -} - -TEST(EstimateMemoryUsageTest, UniquePtr) { - // Empty - { - std::unique_ptr<Data> ptr; - EXPECT_EQ(0u, EstimateMemoryUsage(ptr)); - } - - // Not empty - { - std::unique_ptr<Data> ptr(new Data()); - EXPECT_EQ_32_64(21u, 25u, EstimateMemoryUsage(ptr)); - } - - // With a pointer - { - std::unique_ptr<Data*> ptr(new Data*()); - EXPECT_EQ(sizeof(void*), EstimateMemoryUsage(ptr)); - } - - // With an array - { - struct Item { - uint32_t payload[10]; - }; - std::unique_ptr<Item[]> ptr(new Item[7]); - EXPECT_EQ(280u, EstimateMemoryUsage(ptr, 7)); - } -} - -TEST(EstimateMemoryUsageTest, Vector) { - std::vector<Data> vector; - vector.reserve(1000); - - // For an empty vector we should return memory usage of its buffer - size_t capacity = vector.capacity(); - size_t expected_size = capacity * sizeof(Data); - EXPECT_EQ(expected_size, EstimateMemoryUsage(vector)); - - // If vector is not empty, its size should also include memory usages - // of all elements. - for (size_t i = 0; i != capacity / 2; ++i) { - vector.push_back(Data(i)); - expected_size += EstimateMemoryUsage(vector.back()); - } - EXPECT_EQ(expected_size, EstimateMemoryUsage(vector)); -} - -TEST(EstimateMemoryUsageTest, List) { - struct POD { - short data; - }; - std::list<POD> list; - for (int i = 0; i != 1000; ++i) { - list.push_back(POD()); - } - EXPECT_EQ_32_64(12000u, 24000u, EstimateMemoryUsage(list)); -} - -TEST(EstimateMemoryUsageTest, Set) { - std::set<std::pair<int, Data>> set; - for (int i = 0; i != 1000; ++i) { - set.insert({i, Data(i)}); - } - EXPECT_EQ_32_64(523500u, 547500u, EstimateMemoryUsage(set)); -} - -TEST(EstimateMemoryUsageTest, MultiSet) { - std::multiset<bool> set; - for (int i = 0; i != 1000; ++i) { - set.insert((i & 1) != 0); - } - EXPECT_EQ_32_64(16000u, 32000u, EstimateMemoryUsage(set)); -} - -TEST(EstimateMemoryUsageTest, Map) { - std::map<Data, int> map; - for (int i = 0; i != 1000; ++i) { - map.insert({Data(i), i}); - } - EXPECT_EQ_32_64(523500u, 547500u, EstimateMemoryUsage(map)); -} - -TEST(EstimateMemoryUsageTest, MultiMap) { - std::multimap<char, Data> map; - for (int i = 0; i != 1000; ++i) { - map.insert({static_cast<char>(i), Data(i)}); - } - EXPECT_EQ_32_64(523500u, 547500u, EstimateMemoryUsage(map)); -} - -TEST(EstimateMemoryUsageTest, UnorderedSet) { - std::unordered_set<Data, Data::Hasher> set; - for (int i = 0; i != 1000; ++i) { - set.insert(Data(i)); - } - EXPECT_EQ_32_64(511540u, 523580u, EstimateMemoryUsage(set)); -} - -TEST(EstimateMemoryUsageTest, UnorderedMultiSet) { - std::unordered_multiset<Data, Data::Hasher> set; - for (int i = 0; i != 500; ++i) { - set.insert(Data(i)); - set.insert(Data(i)); - } - EXPECT_EQ_32_64(261540u, 273580u, EstimateMemoryUsage(set)); -} - -TEST(EstimateMemoryUsageTest, UnorderedMap) { - std::unordered_map<Data, short, Data::Hasher> map; - for (int i = 0; i != 1000; ++i) { - map.insert({Data(i), static_cast<short>(i)}); - } - EXPECT_EQ_32_64(515540u, 531580u, EstimateMemoryUsage(map)); -} - -TEST(EstimateMemoryUsageTest, UnorderedMultiMap) { - std::unordered_multimap<Data, short, Data::Hasher> map; - for (int i = 0; i != 1000; ++i) { - map.insert({Data(i), static_cast<short>(i)}); - } - EXPECT_EQ_32_64(515540u, 531580u, EstimateMemoryUsage(map)); -} - -TEST(EstimateMemoryUsageTest, Deque) { - std::deque<Data> deque; - - // Pick a large value so that platform-specific accounting - // for deque's blocks is small compared to usage of all items. - constexpr size_t kDataSize = 100000; - for (int i = 0; i != 1500; ++i) { - deque.push_back(Data(kDataSize)); - } - - // Compare against a reasonable minimum (i.e. no overhead). - size_t min_expected_usage = deque.size() * (sizeof(Data) + kDataSize); - EXPECT_LE(min_expected_usage, EstimateMemoryUsage(deque)); -} - -} // namespace trace_event -} // namespace base diff --git a/base/trace_event/process_memory_dump.cc b/base/trace_event/process_memory_dump.cc deleted file mode 100644 index 63d1340e42..0000000000 --- a/base/trace_event/process_memory_dump.cc +++ /dev/null @@ -1,367 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/trace_event/process_memory_dump.h" - -#include <errno.h> - -#include <vector> - -#include "base/memory/ptr_util.h" -#include "base/process/process_metrics.h" -#include "base/strings/stringprintf.h" -#include "base/trace_event/heap_profiler_heap_dump_writer.h" -#include "base/trace_event/memory_infra_background_whitelist.h" -#include "base/trace_event/process_memory_totals.h" -#include "base/trace_event/trace_event_argument.h" -#include "build/build_config.h" - -#if defined(OS_IOS) -#include <mach/vm_page_size.h> -#endif - -#if defined(OS_POSIX) -#include <sys/mman.h> -#endif - -#if defined(OS_WIN) -#include <Psapi.h> -#endif - -namespace base { -namespace trace_event { - -namespace { - -const char kEdgeTypeOwnership[] = "ownership"; - -std::string GetSharedGlobalAllocatorDumpName( - const MemoryAllocatorDumpGuid& guid) { - return "global/" + guid.ToString(); -} - -#if defined(COUNT_RESIDENT_BYTES_SUPPORTED) -size_t GetSystemPageCount(size_t mapped_size, size_t page_size) { - return (mapped_size + page_size - 1) / page_size; -} -#endif - -} // namespace - -// static -bool ProcessMemoryDump::is_black_hole_non_fatal_for_testing_ = false; - -#if defined(COUNT_RESIDENT_BYTES_SUPPORTED) -// static -size_t ProcessMemoryDump::GetSystemPageSize() { -#if defined(OS_IOS) - // On iOS, getpagesize() returns the user page sizes, but for allocating - // arrays for mincore(), kernel page sizes is needed. Use vm_kernel_page_size - // as recommended by Apple, https://forums.developer.apple.com/thread/47532/. - // Refer to http://crbug.com/542671 and Apple rdar://23651782 - return vm_kernel_page_size; -#else - return base::GetPageSize(); -#endif // defined(OS_IOS) -} - -// static -size_t ProcessMemoryDump::CountResidentBytes(void* start_address, - size_t mapped_size) { - const size_t page_size = GetSystemPageSize(); - const uintptr_t start_pointer = reinterpret_cast<uintptr_t>(start_address); - DCHECK_EQ(0u, start_pointer % page_size); - - size_t offset = 0; - size_t total_resident_size = 0; - bool failure = false; - - // An array as large as number of pages in memory segment needs to be passed - // to the query function. To avoid allocating a large array, the given block - // of memory is split into chunks of size |kMaxChunkSize|. - const size_t kMaxChunkSize = 8 * 1024 * 1024; - size_t max_vec_size = - GetSystemPageCount(std::min(mapped_size, kMaxChunkSize), page_size); -#if defined(OS_MACOSX) || defined(OS_IOS) - std::unique_ptr<char[]> vec(new char[max_vec_size]); -#elif defined(OS_WIN) - std::unique_ptr<PSAPI_WORKING_SET_EX_INFORMATION[]> vec( - new PSAPI_WORKING_SET_EX_INFORMATION[max_vec_size]); -#elif defined(OS_POSIX) - std::unique_ptr<unsigned char[]> vec(new unsigned char[max_vec_size]); -#endif - - while (offset < mapped_size) { - uintptr_t chunk_start = (start_pointer + offset); - const size_t chunk_size = std::min(mapped_size - offset, kMaxChunkSize); - const size_t page_count = GetSystemPageCount(chunk_size, page_size); - size_t resident_page_count = 0; - -#if defined(OS_MACOSX) || defined(OS_IOS) - // mincore in MAC does not fail with EAGAIN. - failure = - !!mincore(reinterpret_cast<void*>(chunk_start), chunk_size, vec.get()); - for (size_t i = 0; i < page_count; i++) - resident_page_count += vec[i] & MINCORE_INCORE ? 1 : 0; -#elif defined(OS_WIN) - for (size_t i = 0; i < page_count; i++) { - vec[i].VirtualAddress = - reinterpret_cast<void*>(chunk_start + i * page_size); - } - DWORD vec_size = static_cast<DWORD>( - page_count * sizeof(PSAPI_WORKING_SET_EX_INFORMATION)); - failure = !QueryWorkingSetEx(GetCurrentProcess(), vec.get(), vec_size); - - for (size_t i = 0; i < page_count; i++) - resident_page_count += vec[i].VirtualAttributes.Valid; -#elif defined(OS_POSIX) - int error_counter = 0; - int result = 0; - // HANDLE_EINTR tries for 100 times. So following the same pattern. - do { - result = - mincore(reinterpret_cast<void*>(chunk_start), chunk_size, vec.get()); - } while (result == -1 && errno == EAGAIN && error_counter++ < 100); - failure = !!result; - - for (size_t i = 0; i < page_count; i++) - resident_page_count += vec[i] & 1; -#endif - - if (failure) - break; - - total_resident_size += resident_page_count * page_size; - offset += kMaxChunkSize; - } - - DCHECK(!failure); - if (failure) { - total_resident_size = 0; - LOG(ERROR) << "CountResidentBytes failed. The resident size is invalid"; - } - return total_resident_size; -} -#endif // defined(COUNT_RESIDENT_BYTES_SUPPORTED) - -ProcessMemoryDump::ProcessMemoryDump( - scoped_refptr<MemoryDumpSessionState> session_state, - const MemoryDumpArgs& dump_args) - : has_process_totals_(false), - has_process_mmaps_(false), - session_state_(std::move(session_state)), - dump_args_(dump_args) {} - -ProcessMemoryDump::~ProcessMemoryDump() {} - -MemoryAllocatorDump* ProcessMemoryDump::CreateAllocatorDump( - const std::string& absolute_name) { - return AddAllocatorDumpInternal( - MakeUnique<MemoryAllocatorDump>(absolute_name, this)); -} - -MemoryAllocatorDump* ProcessMemoryDump::CreateAllocatorDump( - const std::string& absolute_name, - const MemoryAllocatorDumpGuid& guid) { - return AddAllocatorDumpInternal( - MakeUnique<MemoryAllocatorDump>(absolute_name, this, guid)); -} - -MemoryAllocatorDump* ProcessMemoryDump::AddAllocatorDumpInternal( - std::unique_ptr<MemoryAllocatorDump> mad) { - // In background mode return the black hole dump, if invalid dump name is - // given. - if (dump_args_.level_of_detail == MemoryDumpLevelOfDetail::BACKGROUND && - !IsMemoryAllocatorDumpNameWhitelisted(mad->absolute_name())) { - return GetBlackHoleMad(); - } - - auto insertion_result = allocator_dumps_.insert( - std::make_pair(mad->absolute_name(), std::move(mad))); - MemoryAllocatorDump* inserted_mad = insertion_result.first->second.get(); - DCHECK(insertion_result.second) << "Duplicate name: " - << inserted_mad->absolute_name(); - return inserted_mad; -} - -MemoryAllocatorDump* ProcessMemoryDump::GetAllocatorDump( - const std::string& absolute_name) const { - auto it = allocator_dumps_.find(absolute_name); - if (it != allocator_dumps_.end()) - return it->second.get(); - if (black_hole_mad_) - return black_hole_mad_.get(); - return nullptr; -} - -MemoryAllocatorDump* ProcessMemoryDump::GetOrCreateAllocatorDump( - const std::string& absolute_name) { - MemoryAllocatorDump* mad = GetAllocatorDump(absolute_name); - return mad ? mad : CreateAllocatorDump(absolute_name); -} - -MemoryAllocatorDump* ProcessMemoryDump::CreateSharedGlobalAllocatorDump( - const MemoryAllocatorDumpGuid& guid) { - // Global dumps are disabled in background mode. - if (dump_args_.level_of_detail == MemoryDumpLevelOfDetail::BACKGROUND) - return GetBlackHoleMad(); - - // A shared allocator dump can be shared within a process and the guid could - // have been created already. - MemoryAllocatorDump* mad = GetSharedGlobalAllocatorDump(guid); - if (mad) { - // The weak flag is cleared because this method should create a non-weak - // dump. - mad->clear_flags(MemoryAllocatorDump::Flags::WEAK); - return mad; - } - return CreateAllocatorDump(GetSharedGlobalAllocatorDumpName(guid), guid); -} - -MemoryAllocatorDump* ProcessMemoryDump::CreateWeakSharedGlobalAllocatorDump( - const MemoryAllocatorDumpGuid& guid) { - // Global dumps are disabled in background mode. - if (dump_args_.level_of_detail == MemoryDumpLevelOfDetail::BACKGROUND) - return GetBlackHoleMad(); - - MemoryAllocatorDump* mad = GetSharedGlobalAllocatorDump(guid); - if (mad) - return mad; - mad = CreateAllocatorDump(GetSharedGlobalAllocatorDumpName(guid), guid); - mad->set_flags(MemoryAllocatorDump::Flags::WEAK); - return mad; -} - -MemoryAllocatorDump* ProcessMemoryDump::GetSharedGlobalAllocatorDump( - const MemoryAllocatorDumpGuid& guid) const { - return GetAllocatorDump(GetSharedGlobalAllocatorDumpName(guid)); -} - -void ProcessMemoryDump::DumpHeapUsage( - const base::hash_map<base::trace_event::AllocationContext, - base::trace_event::AllocationMetrics>& metrics_by_context, - base::trace_event::TraceEventMemoryOverhead& overhead, - const char* allocator_name) { - if (!metrics_by_context.empty()) { - DCHECK_EQ(0ul, heap_dumps_.count(allocator_name)); - std::unique_ptr<TracedValue> heap_dump = ExportHeapDump( - metrics_by_context, *session_state()); - heap_dumps_[allocator_name] = std::move(heap_dump); - } - - std::string base_name = base::StringPrintf("tracing/heap_profiler_%s", - allocator_name); - overhead.DumpInto(base_name.c_str(), this); -} - -void ProcessMemoryDump::Clear() { - if (has_process_totals_) { - process_totals_.Clear(); - has_process_totals_ = false; - } - - if (has_process_mmaps_) { - process_mmaps_.Clear(); - has_process_mmaps_ = false; - } - - allocator_dumps_.clear(); - allocator_dumps_edges_.clear(); - heap_dumps_.clear(); -} - -void ProcessMemoryDump::TakeAllDumpsFrom(ProcessMemoryDump* other) { - DCHECK(!other->has_process_totals() && !other->has_process_mmaps()); - - // Moves the ownership of all MemoryAllocatorDump(s) contained in |other| - // into this ProcessMemoryDump, checking for duplicates. - for (auto& it : other->allocator_dumps_) - AddAllocatorDumpInternal(std::move(it.second)); - other->allocator_dumps_.clear(); - - // Move all the edges. - allocator_dumps_edges_.insert(allocator_dumps_edges_.end(), - other->allocator_dumps_edges_.begin(), - other->allocator_dumps_edges_.end()); - other->allocator_dumps_edges_.clear(); - - for (auto& it : other->heap_dumps_) { - DCHECK_EQ(0ul, heap_dumps_.count(it.first)); - heap_dumps_.insert(std::make_pair(it.first, std::move(it.second))); - } - other->heap_dumps_.clear(); -} - -void ProcessMemoryDump::AsValueInto(TracedValue* value) const { - if (has_process_totals_) { - value->BeginDictionary("process_totals"); - process_totals_.AsValueInto(value); - value->EndDictionary(); - } - - if (has_process_mmaps_) { - value->BeginDictionary("process_mmaps"); - process_mmaps_.AsValueInto(value); - value->EndDictionary(); - } - - if (allocator_dumps_.size() > 0) { - value->BeginDictionary("allocators"); - for (const auto& allocator_dump_it : allocator_dumps_) - allocator_dump_it.second->AsValueInto(value); - value->EndDictionary(); - } - - if (heap_dumps_.size() > 0) { - value->BeginDictionary("heaps"); - for (const auto& name_and_dump : heap_dumps_) - value->SetValueWithCopiedName(name_and_dump.first, *name_and_dump.second); - value->EndDictionary(); // "heaps" - } - - value->BeginArray("allocators_graph"); - for (const MemoryAllocatorDumpEdge& edge : allocator_dumps_edges_) { - value->BeginDictionary(); - value->SetString("source", edge.source.ToString()); - value->SetString("target", edge.target.ToString()); - value->SetInteger("importance", edge.importance); - value->SetString("type", edge.type); - value->EndDictionary(); - } - value->EndArray(); -} - -void ProcessMemoryDump::AddOwnershipEdge(const MemoryAllocatorDumpGuid& source, - const MemoryAllocatorDumpGuid& target, - int importance) { - allocator_dumps_edges_.push_back( - {source, target, importance, kEdgeTypeOwnership}); -} - -void ProcessMemoryDump::AddOwnershipEdge( - const MemoryAllocatorDumpGuid& source, - const MemoryAllocatorDumpGuid& target) { - AddOwnershipEdge(source, target, 0 /* importance */); -} - -void ProcessMemoryDump::AddSuballocation(const MemoryAllocatorDumpGuid& source, - const std::string& target_node_name) { - // Do not create new dumps for suballocations in background mode. - if (dump_args_.level_of_detail == MemoryDumpLevelOfDetail::BACKGROUND) - return; - - std::string child_mad_name = target_node_name + "/__" + source.ToString(); - MemoryAllocatorDump* target_child_mad = CreateAllocatorDump(child_mad_name); - AddOwnershipEdge(source, target_child_mad->guid()); -} - -MemoryAllocatorDump* ProcessMemoryDump::GetBlackHoleMad() { - DCHECK(is_black_hole_non_fatal_for_testing_); - if (!black_hole_mad_) - black_hole_mad_.reset(new MemoryAllocatorDump("discarded", this)); - return black_hole_mad_.get(); -} - -} // namespace trace_event -} // namespace base diff --git a/base/trace_event/process_memory_dump.h b/base/trace_event/process_memory_dump.h deleted file mode 100644 index 6f8d167273..0000000000 --- a/base/trace_event/process_memory_dump.h +++ /dev/null @@ -1,222 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_TRACE_EVENT_PROCESS_MEMORY_DUMP_H_ -#define BASE_TRACE_EVENT_PROCESS_MEMORY_DUMP_H_ - -#include <stddef.h> - -#include <unordered_map> -#include <vector> - -#include "base/base_export.h" -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "base/memory/scoped_vector.h" -#include "base/trace_event/memory_allocator_dump.h" -#include "base/trace_event/memory_allocator_dump_guid.h" -#include "base/trace_event/memory_dump_request_args.h" -#include "base/trace_event/memory_dump_session_state.h" -#include "base/trace_event/process_memory_maps.h" -#include "base/trace_event/process_memory_totals.h" -#include "build/build_config.h" - -// Define COUNT_RESIDENT_BYTES_SUPPORTED if platform supports counting of the -// resident memory. -#if (defined(OS_POSIX) && !defined(OS_NACL)) || defined(OS_WIN) -#define COUNT_RESIDENT_BYTES_SUPPORTED -#endif - -namespace base { -namespace trace_event { - -class MemoryDumpSessionState; -class TracedValue; - -// ProcessMemoryDump is as a strongly typed container which holds the dumps -// produced by the MemoryDumpProvider(s) for a specific process. -class BASE_EXPORT ProcessMemoryDump { - public: - struct MemoryAllocatorDumpEdge { - MemoryAllocatorDumpGuid source; - MemoryAllocatorDumpGuid target; - int importance; - const char* type; - }; - - // Maps allocator dumps absolute names (allocator_name/heap/subheap) to - // MemoryAllocatorDump instances. - using AllocatorDumpsMap = - std::unordered_map<std::string, std::unique_ptr<MemoryAllocatorDump>>; - - using HeapDumpsMap = - std::unordered_map<std::string, std::unique_ptr<TracedValue>>; - -#if defined(COUNT_RESIDENT_BYTES_SUPPORTED) - // Returns the number of bytes in a kernel memory page. Some platforms may - // have a different value for kernel page sizes from user page sizes. It is - // important to use kernel memory page sizes for resident bytes calculation. - // In most cases, the two are the same. - static size_t GetSystemPageSize(); - - // Returns the total bytes resident for a virtual address range, with given - // |start_address| and |mapped_size|. |mapped_size| is specified in bytes. The - // value returned is valid only if the given range is currently mmapped by the - // process. The |start_address| must be page-aligned. - static size_t CountResidentBytes(void* start_address, size_t mapped_size); -#endif - - ProcessMemoryDump(scoped_refptr<MemoryDumpSessionState> session_state, - const MemoryDumpArgs& dump_args); - ~ProcessMemoryDump(); - - // Creates a new MemoryAllocatorDump with the given name and returns the - // empty object back to the caller. - // Arguments: - // absolute_name: a name that uniquely identifies allocator dumps produced - // by this provider. It is possible to specify nesting by using a - // path-like string (e.g., v8/isolate1/heap1, v8/isolate1/heap2). - // Leading or trailing slashes are not allowed. - // guid: an optional identifier, unique among all processes within the - // scope of a global dump. This is only relevant when using - // AddOwnershipEdge() to express memory sharing. If omitted, - // it will be automatically generated. - // ProcessMemoryDump handles the memory ownership of its MemoryAllocatorDumps. - MemoryAllocatorDump* CreateAllocatorDump(const std::string& absolute_name); - MemoryAllocatorDump* CreateAllocatorDump(const std::string& absolute_name, - const MemoryAllocatorDumpGuid& guid); - - // Looks up a MemoryAllocatorDump given its allocator and heap names, or - // nullptr if not found. - MemoryAllocatorDump* GetAllocatorDump(const std::string& absolute_name) const; - - MemoryAllocatorDump* GetOrCreateAllocatorDump( - const std::string& absolute_name); - - // Creates a shared MemoryAllocatorDump, to express cross-process sharing. - // Shared allocator dumps are allowed to have duplicate guids within the - // global scope, in order to reference the same dump from multiple processes. - // See the design doc goo.gl/keU6Bf for reference usage patterns. - MemoryAllocatorDump* CreateSharedGlobalAllocatorDump( - const MemoryAllocatorDumpGuid& guid); - - // Creates a shared MemoryAllocatorDump as CreateSharedGlobalAllocatorDump, - // but with a WEAK flag. A weak dump will be discarded unless a non-weak dump - // is created using CreateSharedGlobalAllocatorDump by at least one process. - // The WEAK flag does not apply if a non-weak dump with the same GUID already - // exists or is created later. All owners and children of the discarded dump - // will also be discarded transitively. - MemoryAllocatorDump* CreateWeakSharedGlobalAllocatorDump( - const MemoryAllocatorDumpGuid& guid); - - // Looks up a shared MemoryAllocatorDump given its guid. - MemoryAllocatorDump* GetSharedGlobalAllocatorDump( - const MemoryAllocatorDumpGuid& guid) const; - - // Returns the map of the MemoryAllocatorDumps added to this dump. - const AllocatorDumpsMap& allocator_dumps() const { return allocator_dumps_; } - - // Dumps heap usage with |allocator_name|. - void DumpHeapUsage(const base::hash_map<base::trace_event::AllocationContext, - base::trace_event::AllocationMetrics>& - metrics_by_context, - base::trace_event::TraceEventMemoryOverhead& overhead, - const char* allocator_name); - - // Adds an ownership relationship between two MemoryAllocatorDump(s) with the - // semantics: |source| owns |target|, and has the effect of attributing - // the memory usage of |target| to |source|. |importance| is optional and - // relevant only for the cases of co-ownership, where it acts as a z-index: - // the owner with the highest importance will be attributed |target|'s memory. - void AddOwnershipEdge(const MemoryAllocatorDumpGuid& source, - const MemoryAllocatorDumpGuid& target, - int importance); - void AddOwnershipEdge(const MemoryAllocatorDumpGuid& source, - const MemoryAllocatorDumpGuid& target); - - const std::vector<MemoryAllocatorDumpEdge>& allocator_dumps_edges() const { - return allocator_dumps_edges_; - } - - // Utility method to add a suballocation relationship with the following - // semantics: |source| is suballocated from |target_node_name|. - // This creates a child node of |target_node_name| and adds an ownership edge - // between |source| and the new child node. As a result, the UI will not - // account the memory of |source| in the target node. - void AddSuballocation(const MemoryAllocatorDumpGuid& source, - const std::string& target_node_name); - - const scoped_refptr<MemoryDumpSessionState>& session_state() const { - return session_state_; - } - - // Removes all the MemoryAllocatorDump(s) contained in this instance. This - // ProcessMemoryDump can be safely reused as if it was new once this returns. - void Clear(); - - // Merges all MemoryAllocatorDump(s) contained in |other| inside this - // ProcessMemoryDump, transferring their ownership to this instance. - // |other| will be an empty ProcessMemoryDump after this method returns. - // This is to allow dump providers to pre-populate ProcessMemoryDump instances - // and later move their contents into the ProcessMemoryDump passed as argument - // of the MemoryDumpProvider::OnMemoryDump(ProcessMemoryDump*) callback. - void TakeAllDumpsFrom(ProcessMemoryDump* other); - - // Called at trace generation time to populate the TracedValue. - void AsValueInto(TracedValue* value) const; - - ProcessMemoryTotals* process_totals() { return &process_totals_; } - bool has_process_totals() const { return has_process_totals_; } - void set_has_process_totals() { has_process_totals_ = true; } - - ProcessMemoryMaps* process_mmaps() { return &process_mmaps_; } - bool has_process_mmaps() const { return has_process_mmaps_; } - void set_has_process_mmaps() { has_process_mmaps_ = true; } - - const HeapDumpsMap& heap_dumps() const { return heap_dumps_; } - - const MemoryDumpArgs& dump_args() const { return dump_args_; } - - private: - FRIEND_TEST_ALL_PREFIXES(ProcessMemoryDumpTest, BackgroundModeTest); - - MemoryAllocatorDump* AddAllocatorDumpInternal( - std::unique_ptr<MemoryAllocatorDump> mad); - - MemoryAllocatorDump* GetBlackHoleMad(); - - ProcessMemoryTotals process_totals_; - bool has_process_totals_; - - ProcessMemoryMaps process_mmaps_; - bool has_process_mmaps_; - - AllocatorDumpsMap allocator_dumps_; - HeapDumpsMap heap_dumps_; - - // State shared among all PMDs instances created in a given trace session. - scoped_refptr<MemoryDumpSessionState> session_state_; - - // Keeps track of relationships between MemoryAllocatorDump(s). - std::vector<MemoryAllocatorDumpEdge> allocator_dumps_edges_; - - // Level of detail of the current dump. - const MemoryDumpArgs dump_args_; - - // This allocator dump is returned when an invalid dump is created in - // background mode. The attributes of the dump are ignored and not added to - // the trace. - std::unique_ptr<MemoryAllocatorDump> black_hole_mad_; - - // When set to true, the DCHECK(s) for invalid dump creations on the - // background mode are disabled for testing. - static bool is_black_hole_non_fatal_for_testing_; - - DISALLOW_COPY_AND_ASSIGN(ProcessMemoryDump); -}; - -} // namespace trace_event -} // namespace base - -#endif // BASE_TRACE_EVENT_PROCESS_MEMORY_DUMP_H_ diff --git a/base/trace_event/process_memory_dump_unittest.cc b/base/trace_event/process_memory_dump_unittest.cc deleted file mode 100644 index 571774a10c..0000000000 --- a/base/trace_event/process_memory_dump_unittest.cc +++ /dev/null @@ -1,306 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/trace_event/process_memory_dump.h" - -#include <stddef.h> - -#include "base/memory/aligned_memory.h" -#include "base/memory/ptr_util.h" -#include "base/process/process_metrics.h" -#include "base/trace_event/memory_allocator_dump_guid.h" -#include "base/trace_event/memory_infra_background_whitelist.h" -#include "base/trace_event/trace_event_argument.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { -namespace trace_event { - -namespace { - -const MemoryDumpArgs kDetailedDumpArgs = {MemoryDumpLevelOfDetail::DETAILED}; -const char* const kTestDumpNameWhitelist[] = { - "Whitelisted/TestName", "Whitelisted/TestName_0x?", - "Whitelisted/0x?/TestName", nullptr}; - -TracedValue* GetHeapDump(const ProcessMemoryDump& pmd, const char* name) { - auto it = pmd.heap_dumps().find(name); - return it == pmd.heap_dumps().end() ? nullptr : it->second.get(); -} - -} // namespace - -TEST(ProcessMemoryDumpTest, Clear) { - std::unique_ptr<ProcessMemoryDump> pmd1( - new ProcessMemoryDump(nullptr, kDetailedDumpArgs)); - pmd1->CreateAllocatorDump("mad1"); - pmd1->CreateAllocatorDump("mad2"); - ASSERT_FALSE(pmd1->allocator_dumps().empty()); - - pmd1->process_totals()->set_resident_set_bytes(42); - pmd1->set_has_process_totals(); - - pmd1->process_mmaps()->AddVMRegion(ProcessMemoryMaps::VMRegion()); - pmd1->set_has_process_mmaps(); - - pmd1->AddOwnershipEdge(MemoryAllocatorDumpGuid(42), - MemoryAllocatorDumpGuid(4242)); - - MemoryAllocatorDumpGuid shared_mad_guid1(1); - MemoryAllocatorDumpGuid shared_mad_guid2(2); - pmd1->CreateSharedGlobalAllocatorDump(shared_mad_guid1); - pmd1->CreateSharedGlobalAllocatorDump(shared_mad_guid2); - - pmd1->Clear(); - ASSERT_TRUE(pmd1->allocator_dumps().empty()); - ASSERT_TRUE(pmd1->allocator_dumps_edges().empty()); - ASSERT_EQ(nullptr, pmd1->GetAllocatorDump("mad1")); - ASSERT_EQ(nullptr, pmd1->GetAllocatorDump("mad2")); - ASSERT_FALSE(pmd1->has_process_totals()); - ASSERT_FALSE(pmd1->has_process_mmaps()); - ASSERT_TRUE(pmd1->process_mmaps()->vm_regions().empty()); - ASSERT_EQ(nullptr, pmd1->GetSharedGlobalAllocatorDump(shared_mad_guid1)); - ASSERT_EQ(nullptr, pmd1->GetSharedGlobalAllocatorDump(shared_mad_guid2)); - - // Check that calling AsValueInto() doesn't cause a crash. - std::unique_ptr<TracedValue> traced_value(new TracedValue); - pmd1->AsValueInto(traced_value.get()); - - // Check that the pmd can be reused and behaves as expected. - auto* mad1 = pmd1->CreateAllocatorDump("mad1"); - auto* mad3 = pmd1->CreateAllocatorDump("mad3"); - auto* shared_mad1 = pmd1->CreateSharedGlobalAllocatorDump(shared_mad_guid1); - auto* shared_mad2 = - pmd1->CreateWeakSharedGlobalAllocatorDump(shared_mad_guid2); - ASSERT_EQ(4u, pmd1->allocator_dumps().size()); - ASSERT_EQ(mad1, pmd1->GetAllocatorDump("mad1")); - ASSERT_EQ(nullptr, pmd1->GetAllocatorDump("mad2")); - ASSERT_EQ(mad3, pmd1->GetAllocatorDump("mad3")); - ASSERT_EQ(shared_mad1, pmd1->GetSharedGlobalAllocatorDump(shared_mad_guid1)); - ASSERT_EQ(MemoryAllocatorDump::Flags::DEFAULT, shared_mad1->flags()); - ASSERT_EQ(shared_mad2, pmd1->GetSharedGlobalAllocatorDump(shared_mad_guid2)); - ASSERT_EQ(MemoryAllocatorDump::Flags::WEAK, shared_mad2->flags()); - - traced_value.reset(new TracedValue); - pmd1->AsValueInto(traced_value.get()); - - pmd1.reset(); -} - -TEST(ProcessMemoryDumpTest, TakeAllDumpsFrom) { - std::unique_ptr<TracedValue> traced_value(new TracedValue); - hash_map<AllocationContext, AllocationMetrics> metrics_by_context; - metrics_by_context[AllocationContext()] = { 1, 1 }; - TraceEventMemoryOverhead overhead; - - scoped_refptr<MemoryDumpSessionState> session_state = - new MemoryDumpSessionState; - session_state->SetStackFrameDeduplicator( - WrapUnique(new StackFrameDeduplicator)); - session_state->SetTypeNameDeduplicator( - WrapUnique(new TypeNameDeduplicator)); - std::unique_ptr<ProcessMemoryDump> pmd1( - new ProcessMemoryDump(session_state.get(), kDetailedDumpArgs)); - auto* mad1_1 = pmd1->CreateAllocatorDump("pmd1/mad1"); - auto* mad1_2 = pmd1->CreateAllocatorDump("pmd1/mad2"); - pmd1->AddOwnershipEdge(mad1_1->guid(), mad1_2->guid()); - pmd1->DumpHeapUsage(metrics_by_context, overhead, "pmd1/heap_dump1"); - pmd1->DumpHeapUsage(metrics_by_context, overhead, "pmd1/heap_dump2"); - - std::unique_ptr<ProcessMemoryDump> pmd2( - new ProcessMemoryDump(session_state.get(), kDetailedDumpArgs)); - auto* mad2_1 = pmd2->CreateAllocatorDump("pmd2/mad1"); - auto* mad2_2 = pmd2->CreateAllocatorDump("pmd2/mad2"); - pmd2->AddOwnershipEdge(mad2_1->guid(), mad2_2->guid()); - pmd2->DumpHeapUsage(metrics_by_context, overhead, "pmd2/heap_dump1"); - pmd2->DumpHeapUsage(metrics_by_context, overhead, "pmd2/heap_dump2"); - - MemoryAllocatorDumpGuid shared_mad_guid1(1); - MemoryAllocatorDumpGuid shared_mad_guid2(2); - auto* shared_mad1 = pmd2->CreateSharedGlobalAllocatorDump(shared_mad_guid1); - auto* shared_mad2 = - pmd2->CreateWeakSharedGlobalAllocatorDump(shared_mad_guid2); - - pmd1->TakeAllDumpsFrom(pmd2.get()); - - // Make sure that pmd2 is empty but still usable after it has been emptied. - ASSERT_TRUE(pmd2->allocator_dumps().empty()); - ASSERT_TRUE(pmd2->allocator_dumps_edges().empty()); - ASSERT_TRUE(pmd2->heap_dumps().empty()); - pmd2->CreateAllocatorDump("pmd2/this_mad_stays_with_pmd2"); - ASSERT_EQ(1u, pmd2->allocator_dumps().size()); - ASSERT_EQ(1u, pmd2->allocator_dumps().count("pmd2/this_mad_stays_with_pmd2")); - pmd2->AddOwnershipEdge(MemoryAllocatorDumpGuid(42), - MemoryAllocatorDumpGuid(4242)); - - // Check that calling AsValueInto() doesn't cause a crash. - pmd2->AsValueInto(traced_value.get()); - - // Free the |pmd2| to check that the memory ownership of the two MAD(s) - // has been transferred to |pmd1|. - pmd2.reset(); - - // Now check that |pmd1| has been effectively merged. - ASSERT_EQ(6u, pmd1->allocator_dumps().size()); - ASSERT_EQ(1u, pmd1->allocator_dumps().count("pmd1/mad1")); - ASSERT_EQ(1u, pmd1->allocator_dumps().count("pmd1/mad2")); - ASSERT_EQ(1u, pmd1->allocator_dumps().count("pmd2/mad1")); - ASSERT_EQ(1u, pmd1->allocator_dumps().count("pmd1/mad2")); - ASSERT_EQ(2u, pmd1->allocator_dumps_edges().size()); - ASSERT_EQ(shared_mad1, pmd1->GetSharedGlobalAllocatorDump(shared_mad_guid1)); - ASSERT_EQ(shared_mad2, pmd1->GetSharedGlobalAllocatorDump(shared_mad_guid2)); - ASSERT_TRUE(MemoryAllocatorDump::Flags::WEAK & shared_mad2->flags()); - ASSERT_EQ(4u, pmd1->heap_dumps().size()); - ASSERT_TRUE(GetHeapDump(*pmd1, "pmd1/heap_dump1") != nullptr); - ASSERT_TRUE(GetHeapDump(*pmd1, "pmd1/heap_dump2") != nullptr); - ASSERT_TRUE(GetHeapDump(*pmd1, "pmd2/heap_dump1") != nullptr); - ASSERT_TRUE(GetHeapDump(*pmd1, "pmd2/heap_dump2") != nullptr); - - // Check that calling AsValueInto() doesn't cause a crash. - traced_value.reset(new TracedValue); - pmd1->AsValueInto(traced_value.get()); - - pmd1.reset(); -} - -TEST(ProcessMemoryDumpTest, Suballocations) { - std::unique_ptr<ProcessMemoryDump> pmd( - new ProcessMemoryDump(nullptr, kDetailedDumpArgs)); - const std::string allocator_dump_name = "fakealloc/allocated_objects"; - pmd->CreateAllocatorDump(allocator_dump_name); - - // Create one allocation with an auto-assigned guid and mark it as a - // suballocation of "fakealloc/allocated_objects". - auto* pic1_dump = pmd->CreateAllocatorDump("picturemanager/picture1"); - pmd->AddSuballocation(pic1_dump->guid(), allocator_dump_name); - - // Same here, but this time create an allocation with an explicit guid. - auto* pic2_dump = pmd->CreateAllocatorDump("picturemanager/picture2", - MemoryAllocatorDumpGuid(0x42)); - pmd->AddSuballocation(pic2_dump->guid(), allocator_dump_name); - - // Now check that AddSuballocation() has created anonymous child dumps under - // "fakealloc/allocated_objects". - auto anon_node_1_it = pmd->allocator_dumps().find( - allocator_dump_name + "/__" + pic1_dump->guid().ToString()); - ASSERT_NE(pmd->allocator_dumps().end(), anon_node_1_it); - - auto anon_node_2_it = - pmd->allocator_dumps().find(allocator_dump_name + "/__42"); - ASSERT_NE(pmd->allocator_dumps().end(), anon_node_2_it); - - // Finally check that AddSuballocation() has created also the - // edges between the pictures and the anonymous allocator child dumps. - bool found_edge[2]{false, false}; - for (const auto& e : pmd->allocator_dumps_edges()) { - found_edge[0] |= (e.source == pic1_dump->guid() && - e.target == anon_node_1_it->second->guid()); - found_edge[1] |= (e.source == pic2_dump->guid() && - e.target == anon_node_2_it->second->guid()); - } - ASSERT_TRUE(found_edge[0]); - ASSERT_TRUE(found_edge[1]); - - // Check that calling AsValueInto() doesn't cause a crash. - std::unique_ptr<TracedValue> traced_value(new TracedValue); - pmd->AsValueInto(traced_value.get()); - - pmd.reset(); -} - -TEST(ProcessMemoryDumpTest, GlobalAllocatorDumpTest) { - std::unique_ptr<ProcessMemoryDump> pmd( - new ProcessMemoryDump(nullptr, kDetailedDumpArgs)); - MemoryAllocatorDumpGuid shared_mad_guid(1); - auto* shared_mad1 = pmd->CreateWeakSharedGlobalAllocatorDump(shared_mad_guid); - ASSERT_EQ(shared_mad_guid, shared_mad1->guid()); - ASSERT_EQ(MemoryAllocatorDump::Flags::WEAK, shared_mad1->flags()); - - auto* shared_mad2 = pmd->GetSharedGlobalAllocatorDump(shared_mad_guid); - ASSERT_EQ(shared_mad1, shared_mad2); - ASSERT_EQ(MemoryAllocatorDump::Flags::WEAK, shared_mad1->flags()); - - auto* shared_mad3 = pmd->CreateWeakSharedGlobalAllocatorDump(shared_mad_guid); - ASSERT_EQ(shared_mad1, shared_mad3); - ASSERT_EQ(MemoryAllocatorDump::Flags::WEAK, shared_mad1->flags()); - - auto* shared_mad4 = pmd->CreateSharedGlobalAllocatorDump(shared_mad_guid); - ASSERT_EQ(shared_mad1, shared_mad4); - ASSERT_EQ(MemoryAllocatorDump::Flags::DEFAULT, shared_mad1->flags()); - - auto* shared_mad5 = pmd->CreateWeakSharedGlobalAllocatorDump(shared_mad_guid); - ASSERT_EQ(shared_mad1, shared_mad5); - ASSERT_EQ(MemoryAllocatorDump::Flags::DEFAULT, shared_mad1->flags()); -} - -TEST(ProcessMemoryDumpTest, BackgroundModeTest) { - MemoryDumpArgs background_args = {MemoryDumpLevelOfDetail::BACKGROUND}; - std::unique_ptr<ProcessMemoryDump> pmd( - new ProcessMemoryDump(nullptr, background_args)); - ProcessMemoryDump::is_black_hole_non_fatal_for_testing_ = true; - SetAllocatorDumpNameWhitelistForTesting(kTestDumpNameWhitelist); - MemoryAllocatorDump* black_hole_mad = pmd->GetBlackHoleMad(); - - // Invalid dump names. - EXPECT_EQ(black_hole_mad, - pmd->CreateAllocatorDump("NotWhitelisted/TestName")); - EXPECT_EQ(black_hole_mad, pmd->CreateAllocatorDump("TestName")); - EXPECT_EQ(black_hole_mad, pmd->CreateAllocatorDump("Whitelisted/Test")); - EXPECT_EQ(black_hole_mad, - pmd->CreateAllocatorDump("Not/Whitelisted/TestName")); - EXPECT_EQ(black_hole_mad, - pmd->CreateAllocatorDump("Whitelisted/TestName/Google")); - EXPECT_EQ(black_hole_mad, - pmd->CreateAllocatorDump("Whitelisted/TestName/0x1a2Google")); - EXPECT_EQ(black_hole_mad, - pmd->CreateAllocatorDump("Whitelisted/TestName/__12/Google")); - - // Global dumps. - MemoryAllocatorDumpGuid guid(1); - EXPECT_EQ(black_hole_mad, pmd->CreateSharedGlobalAllocatorDump(guid)); - EXPECT_EQ(black_hole_mad, pmd->CreateWeakSharedGlobalAllocatorDump(guid)); - EXPECT_EQ(black_hole_mad, pmd->GetSharedGlobalAllocatorDump(guid)); - - // Suballocations. - pmd->AddSuballocation(guid, "malloc/allocated_objects"); - EXPECT_EQ(0u, pmd->allocator_dumps_edges_.size()); - EXPECT_EQ(0u, pmd->allocator_dumps_.size()); - - // Valid dump names. - EXPECT_NE(black_hole_mad, pmd->CreateAllocatorDump("Whitelisted/TestName")); - EXPECT_NE(black_hole_mad, - pmd->CreateAllocatorDump("Whitelisted/TestName_0xA1b2")); - EXPECT_NE(black_hole_mad, - pmd->CreateAllocatorDump("Whitelisted/0xaB/TestName")); - - // GetAllocatorDump is consistent. - EXPECT_EQ(black_hole_mad, pmd->GetAllocatorDump("NotWhitelisted/TestName")); - EXPECT_NE(black_hole_mad, pmd->GetAllocatorDump("Whitelisted/TestName")); -} - -#if defined(COUNT_RESIDENT_BYTES_SUPPORTED) -TEST(ProcessMemoryDumpTest, CountResidentBytes) { - const size_t page_size = ProcessMemoryDump::GetSystemPageSize(); - - // Allocate few page of dirty memory and check if it is resident. - const size_t size1 = 5 * page_size; - std::unique_ptr<char, base::AlignedFreeDeleter> memory1( - static_cast<char*>(base::AlignedAlloc(size1, page_size))); - memset(memory1.get(), 0, size1); - size_t res1 = ProcessMemoryDump::CountResidentBytes(memory1.get(), size1); - ASSERT_EQ(res1, size1); - - // Allocate a large memory segment (> 8Mib). - const size_t kVeryLargeMemorySize = 15 * 1024 * 1024; - std::unique_ptr<char, base::AlignedFreeDeleter> memory2( - static_cast<char*>(base::AlignedAlloc(kVeryLargeMemorySize, page_size))); - memset(memory2.get(), 0, kVeryLargeMemorySize); - size_t res2 = ProcessMemoryDump::CountResidentBytes(memory2.get(), - kVeryLargeMemorySize); - ASSERT_EQ(res2, kVeryLargeMemorySize); -} -#endif // defined(COUNT_RESIDENT_BYTES_SUPPORTED) - -} // namespace trace_event -} // namespace base diff --git a/base/trace_event/process_memory_maps.cc b/base/trace_event/process_memory_maps.cc deleted file mode 100644 index a121239604..0000000000 --- a/base/trace_event/process_memory_maps.cc +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/trace_event/process_memory_maps.h" - -#include "base/format_macros.h" -#include "base/strings/stringprintf.h" -#include "base/trace_event/trace_event_argument.h" - -namespace base { -namespace trace_event { - -// static -const uint32_t ProcessMemoryMaps::VMRegion::kProtectionFlagsRead = 4; -const uint32_t ProcessMemoryMaps::VMRegion::kProtectionFlagsWrite = 2; -const uint32_t ProcessMemoryMaps::VMRegion::kProtectionFlagsExec = 1; -const uint32_t ProcessMemoryMaps::VMRegion::kProtectionFlagsMayshare = 128; - -ProcessMemoryMaps::VMRegion::VMRegion() - : start_address(0), - size_in_bytes(0), - protection_flags(0), - byte_stats_private_dirty_resident(0), - byte_stats_private_clean_resident(0), - byte_stats_shared_dirty_resident(0), - byte_stats_shared_clean_resident(0), - byte_stats_swapped(0), - byte_stats_proportional_resident(0) { -} - -ProcessMemoryMaps::VMRegion::VMRegion(const VMRegion& other) = default; - -ProcessMemoryMaps::ProcessMemoryMaps() { -} - -ProcessMemoryMaps::~ProcessMemoryMaps() { -} - -void ProcessMemoryMaps::AsValueInto(TracedValue* value) const { - static const char kHexFmt[] = "%" PRIx64; - - // Refer to the design doc goo.gl/sxfFY8 for the semantic of these fields. - value->BeginArray("vm_regions"); - for (const auto& region : vm_regions_) { - value->BeginDictionary(); - - value->SetString("sa", StringPrintf(kHexFmt, region.start_address)); - value->SetString("sz", StringPrintf(kHexFmt, region.size_in_bytes)); - value->SetInteger("pf", region.protection_flags); - value->SetString("mf", region.mapped_file); - - value->BeginDictionary("bs"); // byte stats - value->SetString( - "pss", StringPrintf(kHexFmt, region.byte_stats_proportional_resident)); - value->SetString( - "pd", StringPrintf(kHexFmt, region.byte_stats_private_dirty_resident)); - value->SetString( - "pc", StringPrintf(kHexFmt, region.byte_stats_private_clean_resident)); - value->SetString( - "sd", StringPrintf(kHexFmt, region.byte_stats_shared_dirty_resident)); - value->SetString( - "sc", StringPrintf(kHexFmt, region.byte_stats_shared_clean_resident)); - value->SetString("sw", StringPrintf(kHexFmt, region.byte_stats_swapped)); - value->EndDictionary(); - - value->EndDictionary(); - } - value->EndArray(); -} - -void ProcessMemoryMaps::Clear() { - vm_regions_.clear(); -} - -} // namespace trace_event -} // namespace base diff --git a/base/trace_event/process_memory_maps.h b/base/trace_event/process_memory_maps.h deleted file mode 100644 index 6a7367437e..0000000000 --- a/base/trace_event/process_memory_maps.h +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_TRACE_EVENT_PROCESS_MEMORY_MAPS_H_ -#define BASE_TRACE_EVENT_PROCESS_MEMORY_MAPS_H_ - -#include <stdint.h> - -#include <string> -#include <vector> - -#include "base/base_export.h" -#include "base/macros.h" - -namespace base { -namespace trace_event { - -class TracedValue; - -// Data model for process-wide memory stats. -class BASE_EXPORT ProcessMemoryMaps { - public: - struct BASE_EXPORT VMRegion { - static const uint32_t kProtectionFlagsRead; - static const uint32_t kProtectionFlagsWrite; - static const uint32_t kProtectionFlagsExec; - static const uint32_t kProtectionFlagsMayshare; - - VMRegion(); - VMRegion(const VMRegion& other); - - uint64_t start_address; - uint64_t size_in_bytes; - uint32_t protection_flags; - std::string mapped_file; - - // private_dirty_resident + private_clean_resident + shared_dirty_resident + - // shared_clean_resident = resident set size. - uint64_t byte_stats_private_dirty_resident; - uint64_t byte_stats_private_clean_resident; - uint64_t byte_stats_shared_dirty_resident; - uint64_t byte_stats_shared_clean_resident; - - uint64_t byte_stats_swapped; - - // For multiprocess accounting. - uint64_t byte_stats_proportional_resident; - }; - - ProcessMemoryMaps(); - ~ProcessMemoryMaps(); - - void AddVMRegion(const VMRegion& region) { vm_regions_.push_back(region); } - const std::vector<VMRegion>& vm_regions() const { return vm_regions_; } - - // Called at trace generation time to populate the TracedValue. - void AsValueInto(TracedValue* value) const; - - // Clears up all the VMRegion(s) stored. - void Clear(); - - private: - std::vector<VMRegion> vm_regions_; - - DISALLOW_COPY_AND_ASSIGN(ProcessMemoryMaps); -}; - -} // namespace trace_event -} // namespace base - -#endif // BASE_TRACE_EVENT_PROCESS_MEMORY_MAPS_H_ diff --git a/base/trace_event/process_memory_totals.cc b/base/trace_event/process_memory_totals.cc deleted file mode 100644 index de27ab3d9d..0000000000 --- a/base/trace_event/process_memory_totals.cc +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/trace_event/process_memory_totals.h" - -#include "base/format_macros.h" -#include "base/strings/stringprintf.h" -#include "base/trace_event/trace_event_argument.h" - -namespace base { -namespace trace_event { - -ProcessMemoryTotals::ProcessMemoryTotals() - : resident_set_bytes_(0), - peak_resident_set_bytes_(0), - is_peak_rss_resetable_(false) { -} - -ProcessMemoryTotals::~ProcessMemoryTotals() {} - -void ProcessMemoryTotals::AsValueInto(TracedValue* value) const { - value->SetString("resident_set_bytes", - StringPrintf("%" PRIx64, resident_set_bytes_)); - if (peak_resident_set_bytes_ > 0) { - value->SetString("peak_resident_set_bytes", - StringPrintf("%" PRIx64, peak_resident_set_bytes_)); - value->SetBoolean("is_peak_rss_resetable", is_peak_rss_resetable_); - } - - for (const auto it : extra_fields_) { - value->SetString(it.first, StringPrintf("%" PRIx64, it.second)); - } -} - -void ProcessMemoryTotals::Clear() { - resident_set_bytes_ = 0; -} - -void ProcessMemoryTotals::SetExtraFieldInBytes(const char* name, - uint64_t value) { - DCHECK_EQ(0u, extra_fields_.count(name)); - extra_fields_[name] = value; -} - -} // namespace trace_event -} // namespace base diff --git a/base/trace_event/process_memory_totals.h b/base/trace_event/process_memory_totals.h deleted file mode 100644 index 329967a6ee..0000000000 --- a/base/trace_event/process_memory_totals.h +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_TRACE_EVENT_PROCESS_MEMORY_TOTALS_H_ -#define BASE_TRACE_EVENT_PROCESS_MEMORY_TOTALS_H_ - -#include <stdint.h> - -#include <map> - -#include "base/base_export.h" -#include "base/macros.h" - -namespace base { -namespace trace_event { - -class TracedValue; - -// Data model for process-wide memory stats. -class BASE_EXPORT ProcessMemoryTotals { - public: - ProcessMemoryTotals(); - ~ProcessMemoryTotals(); - - // Called at trace generation time to populate the TracedValue. - void AsValueInto(TracedValue* value) const; - - // Clears up all the data collected. - void Clear(); - - uint64_t resident_set_bytes() const { return resident_set_bytes_; } - void set_resident_set_bytes(uint64_t value) { resident_set_bytes_ = value; } - - uint64_t peak_resident_set_bytes() const { return peak_resident_set_bytes_; } - void set_peak_resident_set_bytes(uint64_t value) { - peak_resident_set_bytes_ = value; - } - - // On some platforms (recent linux kernels, see goo.gl/sMvAVz) the peak rss - // can be reset. When is_peak_rss_resettable == true, the peak refers to - // peak from the previous measurement. When false, it is the absolute peak - // since the start of the process. - bool is_peak_rss_resetable() const { return is_peak_rss_resetable_; } - void set_is_peak_rss_resetable(bool value) { is_peak_rss_resetable_ = value; } - - void SetExtraFieldInBytes(const char* name, uint64_t value); - - private: - uint64_t resident_set_bytes_; - uint64_t peak_resident_set_bytes_; - bool is_peak_rss_resetable_; - - // Extra metrics for OS-specific statistics. - std::map<const char*, uint64_t> extra_fields_; - - DISALLOW_COPY_AND_ASSIGN(ProcessMemoryTotals); -}; - -} // namespace trace_event -} // namespace base - -#endif // BASE_TRACE_EVENT_PROCESS_MEMORY_TOTALS_H_ diff --git a/base/trace_event/trace_buffer.cc b/base/trace_event/trace_buffer.cc deleted file mode 100644 index e26e9fd28f..0000000000 --- a/base/trace_event/trace_buffer.cc +++ /dev/null @@ -1,345 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/trace_event/trace_buffer.h" - -#include <memory> -#include <utility> -#include <vector> - -#include "base/macros.h" -#include "base/trace_event/heap_profiler.h" -#include "base/trace_event/trace_event_impl.h" - -namespace base { -namespace trace_event { - -namespace { - -class TraceBufferRingBuffer : public TraceBuffer { - public: - TraceBufferRingBuffer(size_t max_chunks) - : max_chunks_(max_chunks), - recyclable_chunks_queue_(new size_t[queue_capacity()]), - queue_head_(0), - queue_tail_(max_chunks), - current_iteration_index_(0), - current_chunk_seq_(1) { - chunks_.reserve(max_chunks); - for (size_t i = 0; i < max_chunks; ++i) - recyclable_chunks_queue_[i] = i; - } - - std::unique_ptr<TraceBufferChunk> GetChunk(size_t* index) override { - HEAP_PROFILER_SCOPED_IGNORE; - - // Because the number of threads is much less than the number of chunks, - // the queue should never be empty. - DCHECK(!QueueIsEmpty()); - - *index = recyclable_chunks_queue_[queue_head_]; - queue_head_ = NextQueueIndex(queue_head_); - current_iteration_index_ = queue_head_; - - if (*index >= chunks_.size()) - chunks_.resize(*index + 1); - - TraceBufferChunk* chunk = chunks_[*index].release(); - chunks_[*index] = NULL; // Put NULL in the slot of a in-flight chunk. - if (chunk) - chunk->Reset(current_chunk_seq_++); - else - chunk = new TraceBufferChunk(current_chunk_seq_++); - - return std::unique_ptr<TraceBufferChunk>(chunk); - } - - void ReturnChunk(size_t index, - std::unique_ptr<TraceBufferChunk> chunk) override { - // When this method is called, the queue should not be full because it - // can contain all chunks including the one to be returned. - DCHECK(!QueueIsFull()); - DCHECK(chunk); - DCHECK_LT(index, chunks_.size()); - DCHECK(!chunks_[index]); - chunks_[index] = std::move(chunk); - recyclable_chunks_queue_[queue_tail_] = index; - queue_tail_ = NextQueueIndex(queue_tail_); - } - - bool IsFull() const override { return false; } - - size_t Size() const override { - // This is approximate because not all of the chunks are full. - return chunks_.size() * TraceBufferChunk::kTraceBufferChunkSize; - } - - size_t Capacity() const override { - return max_chunks_ * TraceBufferChunk::kTraceBufferChunkSize; - } - - TraceEvent* GetEventByHandle(TraceEventHandle handle) override { - if (handle.chunk_index >= chunks_.size()) - return NULL; - TraceBufferChunk* chunk = chunks_[handle.chunk_index].get(); - if (!chunk || chunk->seq() != handle.chunk_seq) - return NULL; - return chunk->GetEventAt(handle.event_index); - } - - const TraceBufferChunk* NextChunk() override { - if (chunks_.empty()) - return NULL; - - while (current_iteration_index_ != queue_tail_) { - size_t chunk_index = recyclable_chunks_queue_[current_iteration_index_]; - current_iteration_index_ = NextQueueIndex(current_iteration_index_); - if (chunk_index >= chunks_.size()) // Skip uninitialized chunks. - continue; - DCHECK(chunks_[chunk_index]); - return chunks_[chunk_index].get(); - } - return NULL; - } - - void EstimateTraceMemoryOverhead( - TraceEventMemoryOverhead* overhead) override { - overhead->Add("TraceBufferRingBuffer", sizeof(*this)); - for (size_t queue_index = queue_head_; queue_index != queue_tail_; - queue_index = NextQueueIndex(queue_index)) { - size_t chunk_index = recyclable_chunks_queue_[queue_index]; - if (chunk_index >= chunks_.size()) // Skip uninitialized chunks. - continue; - chunks_[chunk_index]->EstimateTraceMemoryOverhead(overhead); - } - } - - private: - bool QueueIsEmpty() const { return queue_head_ == queue_tail_; } - - size_t QueueSize() const { - return queue_tail_ > queue_head_ - ? queue_tail_ - queue_head_ - : queue_tail_ + queue_capacity() - queue_head_; - } - - bool QueueIsFull() const { return QueueSize() == queue_capacity() - 1; } - - size_t queue_capacity() const { - // One extra space to help distinguish full state and empty state. - return max_chunks_ + 1; - } - - size_t NextQueueIndex(size_t index) const { - index++; - if (index >= queue_capacity()) - index = 0; - return index; - } - - size_t max_chunks_; - std::vector<std::unique_ptr<TraceBufferChunk>> chunks_; - - std::unique_ptr<size_t[]> recyclable_chunks_queue_; - size_t queue_head_; - size_t queue_tail_; - - size_t current_iteration_index_; - uint32_t current_chunk_seq_; - - DISALLOW_COPY_AND_ASSIGN(TraceBufferRingBuffer); -}; - -class TraceBufferVector : public TraceBuffer { - public: - TraceBufferVector(size_t max_chunks) - : in_flight_chunk_count_(0), - current_iteration_index_(0), - max_chunks_(max_chunks) { - chunks_.reserve(max_chunks_); - } - - std::unique_ptr<TraceBufferChunk> GetChunk(size_t* index) override { - HEAP_PROFILER_SCOPED_IGNORE; - - // This function may be called when adding normal events or indirectly from - // AddMetadataEventsWhileLocked(). We can not DECHECK(!IsFull()) because we - // have to add the metadata events and flush thread-local buffers even if - // the buffer is full. - *index = chunks_.size(); - // Put nullptr in the slot of a in-flight chunk. - chunks_.push_back(nullptr); - ++in_flight_chunk_count_; - // + 1 because zero chunk_seq is not allowed. - return std::unique_ptr<TraceBufferChunk>( - new TraceBufferChunk(static_cast<uint32_t>(*index) + 1)); - } - - void ReturnChunk(size_t index, - std::unique_ptr<TraceBufferChunk> chunk) override { - DCHECK_GT(in_flight_chunk_count_, 0u); - DCHECK_LT(index, chunks_.size()); - DCHECK(!chunks_[index]); - --in_flight_chunk_count_; - chunks_[index] = std::move(chunk); - } - - bool IsFull() const override { return chunks_.size() >= max_chunks_; } - - size_t Size() const override { - // This is approximate because not all of the chunks are full. - return chunks_.size() * TraceBufferChunk::kTraceBufferChunkSize; - } - - size_t Capacity() const override { - return max_chunks_ * TraceBufferChunk::kTraceBufferChunkSize; - } - - TraceEvent* GetEventByHandle(TraceEventHandle handle) override { - if (handle.chunk_index >= chunks_.size()) - return NULL; - TraceBufferChunk* chunk = chunks_[handle.chunk_index].get(); - if (!chunk || chunk->seq() != handle.chunk_seq) - return NULL; - return chunk->GetEventAt(handle.event_index); - } - - const TraceBufferChunk* NextChunk() override { - while (current_iteration_index_ < chunks_.size()) { - // Skip in-flight chunks. - const TraceBufferChunk* chunk = chunks_[current_iteration_index_++].get(); - if (chunk) - return chunk; - } - return NULL; - } - - void EstimateTraceMemoryOverhead( - TraceEventMemoryOverhead* overhead) override { - const size_t chunks_ptr_vector_allocated_size = - sizeof(*this) + max_chunks_ * sizeof(decltype(chunks_)::value_type); - const size_t chunks_ptr_vector_resident_size = - sizeof(*this) + chunks_.size() * sizeof(decltype(chunks_)::value_type); - overhead->Add("TraceBufferVector", chunks_ptr_vector_allocated_size, - chunks_ptr_vector_resident_size); - for (size_t i = 0; i < chunks_.size(); ++i) { - TraceBufferChunk* chunk = chunks_[i].get(); - // Skip the in-flight (nullptr) chunks. They will be accounted by the - // per-thread-local dumpers, see ThreadLocalEventBuffer::OnMemoryDump. - if (chunk) - chunk->EstimateTraceMemoryOverhead(overhead); - } - } - - private: - size_t in_flight_chunk_count_; - size_t current_iteration_index_; - size_t max_chunks_; - std::vector<std::unique_ptr<TraceBufferChunk>> chunks_; - - DISALLOW_COPY_AND_ASSIGN(TraceBufferVector); -}; - -} // namespace - -TraceBufferChunk::TraceBufferChunk(uint32_t seq) : next_free_(0), seq_(seq) {} - -TraceBufferChunk::~TraceBufferChunk() {} - -void TraceBufferChunk::Reset(uint32_t new_seq) { - for (size_t i = 0; i < next_free_; ++i) - chunk_[i].Reset(); - next_free_ = 0; - seq_ = new_seq; - cached_overhead_estimate_.reset(); -} - -TraceEvent* TraceBufferChunk::AddTraceEvent(size_t* event_index) { - DCHECK(!IsFull()); - *event_index = next_free_++; - return &chunk_[*event_index]; -} - -void TraceBufferChunk::EstimateTraceMemoryOverhead( - TraceEventMemoryOverhead* overhead) { - if (!cached_overhead_estimate_) { - cached_overhead_estimate_.reset(new TraceEventMemoryOverhead); - - // When estimating the size of TraceBufferChunk, exclude the array of trace - // events, as they are computed individually below. - cached_overhead_estimate_->Add("TraceBufferChunk", - sizeof(*this) - sizeof(chunk_)); - } - - const size_t num_cached_estimated_events = - cached_overhead_estimate_->GetCount("TraceEvent"); - DCHECK_LE(num_cached_estimated_events, size()); - - if (IsFull() && num_cached_estimated_events == size()) { - overhead->Update(*cached_overhead_estimate_); - return; - } - - for (size_t i = num_cached_estimated_events; i < size(); ++i) - chunk_[i].EstimateTraceMemoryOverhead(cached_overhead_estimate_.get()); - - if (IsFull()) { - cached_overhead_estimate_->AddSelf(); - } else { - // The unused TraceEvents in |chunks_| are not cached. They will keep - // changing as new TraceEvents are added to this chunk, so they are - // computed on the fly. - const size_t num_unused_trace_events = capacity() - size(); - overhead->Add("TraceEvent (unused)", - num_unused_trace_events * sizeof(TraceEvent)); - } - - overhead->Update(*cached_overhead_estimate_); -} - -TraceResultBuffer::OutputCallback -TraceResultBuffer::SimpleOutput::GetCallback() { - return Bind(&SimpleOutput::Append, Unretained(this)); -} - -void TraceResultBuffer::SimpleOutput::Append( - const std::string& json_trace_output) { - json_output += json_trace_output; -} - -TraceResultBuffer::TraceResultBuffer() : append_comma_(false) {} - -TraceResultBuffer::~TraceResultBuffer() {} - -void TraceResultBuffer::SetOutputCallback( - const OutputCallback& json_chunk_callback) { - output_callback_ = json_chunk_callback; -} - -void TraceResultBuffer::Start() { - append_comma_ = false; - output_callback_.Run("["); -} - -void TraceResultBuffer::AddFragment(const std::string& trace_fragment) { - if (append_comma_) - output_callback_.Run(","); - append_comma_ = true; - output_callback_.Run(trace_fragment); -} - -void TraceResultBuffer::Finish() { - output_callback_.Run("]"); -} - -TraceBuffer* TraceBuffer::CreateTraceBufferRingBuffer(size_t max_chunks) { - return new TraceBufferRingBuffer(max_chunks); -} - -TraceBuffer* TraceBuffer::CreateTraceBufferVectorOfSize(size_t max_chunks) { - return new TraceBufferVector(max_chunks); -} - -} // namespace trace_event -} // namespace base diff --git a/base/trace_event/trace_buffer.h b/base/trace_event/trace_buffer.h deleted file mode 100644 index 4885a3c7c0..0000000000 --- a/base/trace_event/trace_buffer.h +++ /dev/null @@ -1,130 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_TRACE_EVENT_TRACE_BUFFER_H_ -#define BASE_TRACE_EVENT_TRACE_BUFFER_H_ - -#include <stddef.h> -#include <stdint.h> - -#include "base/base_export.h" -#include "base/trace_event/trace_event.h" -#include "base/trace_event/trace_event_impl.h" - -namespace base { - -namespace trace_event { - -// TraceBufferChunk is the basic unit of TraceBuffer. -class BASE_EXPORT TraceBufferChunk { - public: - explicit TraceBufferChunk(uint32_t seq); - ~TraceBufferChunk(); - - void Reset(uint32_t new_seq); - TraceEvent* AddTraceEvent(size_t* event_index); - bool IsFull() const { return next_free_ == kTraceBufferChunkSize; } - - uint32_t seq() const { return seq_; } - size_t capacity() const { return kTraceBufferChunkSize; } - size_t size() const { return next_free_; } - - TraceEvent* GetEventAt(size_t index) { - DCHECK(index < size()); - return &chunk_[index]; - } - const TraceEvent* GetEventAt(size_t index) const { - DCHECK(index < size()); - return &chunk_[index]; - } - - void EstimateTraceMemoryOverhead(TraceEventMemoryOverhead* overhead); - - // These values must be kept consistent with the numbers of bits of - // chunk_index and event_index fields in TraceEventHandle - // (in trace_event_impl.h). - static const size_t kMaxChunkIndex = (1u << 26) - 1; - static const size_t kTraceBufferChunkSize = 64; - - private: - size_t next_free_; - std::unique_ptr<TraceEventMemoryOverhead> cached_overhead_estimate_; - TraceEvent chunk_[kTraceBufferChunkSize]; - uint32_t seq_; -}; - -// TraceBuffer holds the events as they are collected. -class BASE_EXPORT TraceBuffer { - public: - virtual ~TraceBuffer() {} - - virtual std::unique_ptr<TraceBufferChunk> GetChunk(size_t* index) = 0; - virtual void ReturnChunk(size_t index, - std::unique_ptr<TraceBufferChunk> chunk) = 0; - - virtual bool IsFull() const = 0; - virtual size_t Size() const = 0; - virtual size_t Capacity() const = 0; - virtual TraceEvent* GetEventByHandle(TraceEventHandle handle) = 0; - - // For iteration. Each TraceBuffer can only be iterated once. - virtual const TraceBufferChunk* NextChunk() = 0; - - - // Computes an estimate of the size of the buffer, including all the retained - // objects. - virtual void EstimateTraceMemoryOverhead( - TraceEventMemoryOverhead* overhead) = 0; - - static TraceBuffer* CreateTraceBufferRingBuffer(size_t max_chunks); - static TraceBuffer* CreateTraceBufferVectorOfSize(size_t max_chunks); -}; - -// TraceResultBuffer collects and converts trace fragments returned by TraceLog -// to JSON output. -class BASE_EXPORT TraceResultBuffer { - public: - typedef base::Callback<void(const std::string&)> OutputCallback; - - // If you don't need to stream JSON chunks out efficiently, and just want to - // get a complete JSON string after calling Finish, use this struct to collect - // JSON trace output. - struct BASE_EXPORT SimpleOutput { - OutputCallback GetCallback(); - void Append(const std::string& json_string); - - // Do what you want with the json_output_ string after calling - // TraceResultBuffer::Finish. - std::string json_output; - }; - - TraceResultBuffer(); - ~TraceResultBuffer(); - - // Set callback. The callback will be called during Start with the initial - // JSON output and during AddFragment and Finish with following JSON output - // chunks. The callback target must live past the last calls to - // TraceResultBuffer::Start/AddFragment/Finish. - void SetOutputCallback(const OutputCallback& json_chunk_callback); - - // Start JSON output. This resets all internal state, so you can reuse - // the TraceResultBuffer by calling Start. - void Start(); - - // Call AddFragment 0 or more times to add trace fragments from TraceLog. - void AddFragment(const std::string& trace_fragment); - - // When all fragments have been added, call Finish to complete the JSON - // formatted output. - void Finish(); - - private: - OutputCallback output_callback_; - bool append_comma_; -}; - -} // namespace trace_event -} // namespace base - -#endif // BASE_TRACE_EVENT_TRACE_BUFFER_H_ diff --git a/base/trace_event/trace_category.h b/base/trace_event/trace_category.h deleted file mode 100644 index 5a7915ac03..0000000000 --- a/base/trace_event/trace_category.h +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_TRACE_EVENT_TRACE_CATEGORY_H_ -#define BASE_TRACE_EVENT_TRACE_CATEGORY_H_ - -#include <stdint.h> - -namespace base { -namespace trace_event { - -// Captures the state of an invidivual trace category. Nothing except tracing -// internals (e.g., TraceLog) is supposed to have non-const Category pointers. -struct TraceCategory { - // The TRACE_EVENT macros should only use this value as a bool. - // These enum values are effectively a public API and third_party projects - // depend on their value. Hence, never remove or recycle existing bits, unless - // you are sure that all the third-party projects that depend on this have - // been updated. - enum StateFlags : uint8_t { - ENABLED_FOR_RECORDING = 1 << 0, - - // Not used anymore. - DEPRECATED_ENABLED_FOR_MONITORING = 1 << 1, - DEPRECATED_ENABLED_FOR_EVENT_CALLBACK = 1 << 2, - - ENABLED_FOR_ETW_EXPORT = 1 << 3, - ENABLED_FOR_FILTERING = 1 << 4 - }; - - static const TraceCategory* FromStatePtr(const uint8_t* state_ptr) { - static_assert( - offsetof(TraceCategory, state_) == 0, - "|state_| must be the first field of the TraceCategory class."); - return reinterpret_cast<const TraceCategory*>(state_ptr); - } - - bool is_valid() const { return name_ != nullptr; } - void set_name(const char* name) { name_ = name; } - const char* name() const { - DCHECK(is_valid()); - return name_; - } - - // TODO(primiano): This is an intermediate solution to deal with the fact that - // today TRACE_EVENT* macros cache the state ptr. They should just cache the - // full TraceCategory ptr, which is immutable, and use these helper function - // here. This will get rid of the need of this awkward ptr getter completely. - const uint8_t* state_ptr() const { - return const_cast<const uint8_t*>(&state_); - } - - uint8_t state() const { - return *const_cast<volatile const uint8_t*>(&state_); - } - - bool is_enabled() const { return state() != 0; } - - void set_state(uint8_t state) { - *const_cast<volatile uint8_t*>(&state_) = state; - } - - void clear_state_flag(StateFlags flag) { set_state(state() & (~flag)); } - void set_state_flag(StateFlags flag) { set_state(state() | flag); } - - uint32_t enabled_filters() const { - return *const_cast<volatile const uint32_t*>(&enabled_filters_); - } - - bool is_filter_enabled(size_t index) const { - DCHECK(index < sizeof(enabled_filters_) * 8); - return (enabled_filters() & (1 << index)) != 0; - } - - void set_enabled_filters(uint32_t enabled_filters) { - *const_cast<volatile uint32_t*>(&enabled_filters_) = enabled_filters; - } - - void reset_for_testing() { - set_state(0); - set_enabled_filters(0); - } - - // These fields should not be accessed directly, not even by tracing code. - // The only reason why these are not private is because it makes it impossible - // to have a global array of TraceCategory in category_registry.cc without - // creating initializers. See discussion on goo.gl/qhZN94 and - // crbug.com/{660967,660828}. - - // The enabled state. TRACE_EVENT* macros will capture events if any of the - // flags here are set. Since TRACE_EVENTx macros are used in a lot of - // fast-paths, accesses to this field are non-barriered and racy by design. - // This field is mutated when starting/stopping tracing and we don't care - // about missing some events. - uint8_t state_; - - // When ENABLED_FOR_FILTERING is set, this contains a bitmap to the - // coressponding filter (see event_filters.h). - uint32_t enabled_filters_; - - // TraceCategory group names are long lived static strings. - const char* name_; -}; - -} // namespace trace_event -} // namespace base - -#endif // BASE_TRACE_EVENT_TRACE_CATEGORY_H_ diff --git a/base/trace_event/trace_config.cc b/base/trace_event/trace_config.cc deleted file mode 100644 index 7ee9a4a101..0000000000 --- a/base/trace_event/trace_config.cc +++ /dev/null @@ -1,592 +0,0 @@ -// Copyright (c) 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/trace_event/trace_config.h" - -#include <stddef.h> - -#include <utility> - -#include "base/json/json_reader.h" -#include "base/json/json_writer.h" -#include "base/memory/ptr_util.h" -#include "base/strings/string_split.h" -#include "base/trace_event/memory_dump_manager.h" -#include "base/trace_event/memory_dump_request_args.h" -#include "base/trace_event/trace_event.h" - -namespace base { -namespace trace_event { - -namespace { - -// String options that can be used to initialize TraceOptions. -const char kRecordUntilFull[] = "record-until-full"; -const char kRecordContinuously[] = "record-continuously"; -const char kRecordAsMuchAsPossible[] = "record-as-much-as-possible"; -const char kTraceToConsole[] = "trace-to-console"; -const char kEnableSystrace[] = "enable-systrace"; -const char kEnableArgumentFilter[] = "enable-argument-filter"; - -// String parameters that can be used to parse the trace config string. -const char kRecordModeParam[] = "record_mode"; -const char kEnableSystraceParam[] = "enable_systrace"; -const char kEnableArgumentFilterParam[] = "enable_argument_filter"; - -// String parameters that is used to parse memory dump config in trace config -// string. -const char kMemoryDumpConfigParam[] = "memory_dump_config"; -const char kAllowedDumpModesParam[] = "allowed_dump_modes"; -const char kTriggersParam[] = "triggers"; -const char kTriggerModeParam[] = "mode"; -const char kMinTimeBetweenDumps[] = "min_time_between_dumps_ms"; -const char kTriggerTypeParam[] = "type"; -const char kPeriodicIntervalLegacyParam[] = "periodic_interval_ms"; -const char kHeapProfilerOptions[] = "heap_profiler_options"; -const char kBreakdownThresholdBytes[] = "breakdown_threshold_bytes"; - -// String parameters used to parse category event filters. -const char kEventFiltersParam[] = "event_filters"; -const char kFilterPredicateParam[] = "filter_predicate"; -const char kFilterArgsParam[] = "filter_args"; - -// Default configuration of memory dumps. -const TraceConfig::MemoryDumpConfig::Trigger kDefaultHeavyMemoryDumpTrigger = { - 2000, // min_time_between_dumps_ms - MemoryDumpLevelOfDetail::DETAILED, MemoryDumpType::PERIODIC_INTERVAL}; -const TraceConfig::MemoryDumpConfig::Trigger kDefaultLightMemoryDumpTrigger = { - 250, // min_time_between_dumps_ms - MemoryDumpLevelOfDetail::LIGHT, MemoryDumpType::PERIODIC_INTERVAL}; - -class ConvertableTraceConfigToTraceFormat - : public base::trace_event::ConvertableToTraceFormat { - public: - explicit ConvertableTraceConfigToTraceFormat(const TraceConfig& trace_config) - : trace_config_(trace_config) {} - - ~ConvertableTraceConfigToTraceFormat() override {} - - void AppendAsTraceFormat(std::string* out) const override { - out->append(trace_config_.ToString()); - } - - private: - const TraceConfig trace_config_; -}; - -std::set<MemoryDumpLevelOfDetail> GetDefaultAllowedMemoryDumpModes() { - std::set<MemoryDumpLevelOfDetail> all_modes; - for (uint32_t mode = static_cast<uint32_t>(MemoryDumpLevelOfDetail::FIRST); - mode <= static_cast<uint32_t>(MemoryDumpLevelOfDetail::LAST); mode++) { - all_modes.insert(static_cast<MemoryDumpLevelOfDetail>(mode)); - } - return all_modes; -} - -} // namespace - -TraceConfig::MemoryDumpConfig::HeapProfiler::HeapProfiler() - : breakdown_threshold_bytes(kDefaultBreakdownThresholdBytes) {} - -void TraceConfig::MemoryDumpConfig::HeapProfiler::Clear() { - breakdown_threshold_bytes = kDefaultBreakdownThresholdBytes; -} - -void TraceConfig::ResetMemoryDumpConfig( - const TraceConfig::MemoryDumpConfig& memory_dump_config) { - memory_dump_config_.Clear(); - memory_dump_config_ = memory_dump_config; -} - -TraceConfig::MemoryDumpConfig::MemoryDumpConfig() {} - -TraceConfig::MemoryDumpConfig::MemoryDumpConfig( - const MemoryDumpConfig& other) = default; - -TraceConfig::MemoryDumpConfig::~MemoryDumpConfig() {} - -void TraceConfig::MemoryDumpConfig::Clear() { - allowed_dump_modes.clear(); - triggers.clear(); - heap_profiler_options.Clear(); -} - -void TraceConfig::MemoryDumpConfig::Merge( - const TraceConfig::MemoryDumpConfig& config) { - triggers.insert(triggers.end(), config.triggers.begin(), - config.triggers.end()); - allowed_dump_modes.insert(config.allowed_dump_modes.begin(), - config.allowed_dump_modes.end()); - heap_profiler_options.breakdown_threshold_bytes = - std::min(heap_profiler_options.breakdown_threshold_bytes, - config.heap_profiler_options.breakdown_threshold_bytes); -} - -TraceConfig::EventFilterConfig::EventFilterConfig( - const std::string& predicate_name) - : predicate_name_(predicate_name) {} - -TraceConfig::EventFilterConfig::~EventFilterConfig() {} - -TraceConfig::EventFilterConfig::EventFilterConfig(const EventFilterConfig& tc) { - *this = tc; -} - -TraceConfig::EventFilterConfig& TraceConfig::EventFilterConfig::operator=( - const TraceConfig::EventFilterConfig& rhs) { - if (this == &rhs) - return *this; - - predicate_name_ = rhs.predicate_name_; - category_filter_ = rhs.category_filter_; - - if (rhs.args_) - args_ = rhs.args_->CreateDeepCopy(); - - return *this; -} - -void TraceConfig::EventFilterConfig::InitializeFromConfigDict( - const base::DictionaryValue* event_filter) { - category_filter_.InitializeFromConfigDict(*event_filter); - - const base::DictionaryValue* args_dict = nullptr; - if (event_filter->GetDictionary(kFilterArgsParam, &args_dict)) - args_ = args_dict->CreateDeepCopy(); -} - -void TraceConfig::EventFilterConfig::SetCategoryFilter( - const TraceConfigCategoryFilter& category_filter) { - category_filter_ = category_filter; -} - -void TraceConfig::EventFilterConfig::ToDict( - DictionaryValue* filter_dict) const { - filter_dict->SetString(kFilterPredicateParam, predicate_name()); - - category_filter_.ToDict(filter_dict); - - if (args_) - filter_dict->Set(kFilterArgsParam, args_->CreateDeepCopy()); -} - -bool TraceConfig::EventFilterConfig::GetArgAsSet( - const char* key, - std::unordered_set<std::string>* out_set) const { - const ListValue* list = nullptr; - if (!args_->GetList(key, &list)) - return false; - for (size_t i = 0; i < list->GetSize(); ++i) { - std::string value; - if (list->GetString(i, &value)) - out_set->insert(value); - } - return true; -} - -bool TraceConfig::EventFilterConfig::IsCategoryGroupEnabled( - const StringPiece& category_group_name) const { - return category_filter_.IsCategoryGroupEnabled(category_group_name); -} - -TraceConfig::TraceConfig() { - InitializeDefault(); -} - -TraceConfig::TraceConfig(StringPiece category_filter_string, - StringPiece trace_options_string) { - InitializeFromStrings(category_filter_string, trace_options_string); -} - -TraceConfig::TraceConfig(StringPiece category_filter_string, - TraceRecordMode record_mode) { - std::string trace_options_string; - switch (record_mode) { - case RECORD_UNTIL_FULL: - trace_options_string = kRecordUntilFull; - break; - case RECORD_CONTINUOUSLY: - trace_options_string = kRecordContinuously; - break; - case RECORD_AS_MUCH_AS_POSSIBLE: - trace_options_string = kRecordAsMuchAsPossible; - break; - case ECHO_TO_CONSOLE: - trace_options_string = kTraceToConsole; - break; - default: - NOTREACHED(); - } - InitializeFromStrings(category_filter_string, trace_options_string); -} - -TraceConfig::TraceConfig(const DictionaryValue& config) { - InitializeFromConfigDict(config); -} - -TraceConfig::TraceConfig(StringPiece config_string) { - if (!config_string.empty()) - InitializeFromConfigString(config_string); - else - InitializeDefault(); -} - -TraceConfig::TraceConfig(const TraceConfig& tc) - : record_mode_(tc.record_mode_), - enable_systrace_(tc.enable_systrace_), - enable_argument_filter_(tc.enable_argument_filter_), - category_filter_(tc.category_filter_), - memory_dump_config_(tc.memory_dump_config_), - event_filters_(tc.event_filters_) {} - -TraceConfig::~TraceConfig() { -} - -TraceConfig& TraceConfig::operator=(const TraceConfig& rhs) { - if (this == &rhs) - return *this; - - record_mode_ = rhs.record_mode_; - enable_systrace_ = rhs.enable_systrace_; - enable_argument_filter_ = rhs.enable_argument_filter_; - category_filter_ = rhs.category_filter_; - memory_dump_config_ = rhs.memory_dump_config_; - event_filters_ = rhs.event_filters_; - return *this; -} - -const TraceConfig::StringList& TraceConfig::GetSyntheticDelayValues() const { - return category_filter_.synthetic_delays(); -} - -std::string TraceConfig::ToString() const { - std::unique_ptr<DictionaryValue> dict = ToDict(); - std::string json; - JSONWriter::Write(*dict, &json); - return json; -} - -std::unique_ptr<ConvertableToTraceFormat> -TraceConfig::AsConvertableToTraceFormat() const { - return MakeUnique<ConvertableTraceConfigToTraceFormat>(*this); -} - -std::string TraceConfig::ToCategoryFilterString() const { - return category_filter_.ToFilterString(); -} - -bool TraceConfig::IsCategoryGroupEnabled( - const StringPiece& category_group_name) const { - // TraceLog should call this method only as part of enabling/disabling - // categories. - return category_filter_.IsCategoryGroupEnabled(category_group_name); -} - -void TraceConfig::Merge(const TraceConfig& config) { - if (record_mode_ != config.record_mode_ - || enable_systrace_ != config.enable_systrace_ - || enable_argument_filter_ != config.enable_argument_filter_) { - DLOG(ERROR) << "Attempting to merge trace config with a different " - << "set of options."; - } - - category_filter_.Merge(config.category_filter_); - - memory_dump_config_.Merge(config.memory_dump_config_); - - event_filters_.insert(event_filters_.end(), config.event_filters().begin(), - config.event_filters().end()); -} - -void TraceConfig::Clear() { - record_mode_ = RECORD_UNTIL_FULL; - enable_systrace_ = false; - enable_argument_filter_ = false; - category_filter_.Clear(); - memory_dump_config_.Clear(); - event_filters_.clear(); -} - -void TraceConfig::InitializeDefault() { - record_mode_ = RECORD_UNTIL_FULL; - enable_systrace_ = false; - enable_argument_filter_ = false; -} - -void TraceConfig::InitializeFromConfigDict(const DictionaryValue& dict) { - record_mode_ = RECORD_UNTIL_FULL; - std::string record_mode; - if (dict.GetString(kRecordModeParam, &record_mode)) { - if (record_mode == kRecordUntilFull) { - record_mode_ = RECORD_UNTIL_FULL; - } else if (record_mode == kRecordContinuously) { - record_mode_ = RECORD_CONTINUOUSLY; - } else if (record_mode == kTraceToConsole) { - record_mode_ = ECHO_TO_CONSOLE; - } else if (record_mode == kRecordAsMuchAsPossible) { - record_mode_ = RECORD_AS_MUCH_AS_POSSIBLE; - } - } - - bool val; - enable_systrace_ = dict.GetBoolean(kEnableSystraceParam, &val) ? val : false; - enable_argument_filter_ = - dict.GetBoolean(kEnableArgumentFilterParam, &val) ? val : false; - - category_filter_.InitializeFromConfigDict(dict); - - const base::ListValue* category_event_filters = nullptr; - if (dict.GetList(kEventFiltersParam, &category_event_filters)) - SetEventFiltersFromConfigList(*category_event_filters); - - if (category_filter_.IsCategoryEnabled(MemoryDumpManager::kTraceCategory)) { - // If dump triggers not set, the client is using the legacy with just - // category enabled. So, use the default periodic dump config. - const DictionaryValue* memory_dump_config = nullptr; - if (dict.GetDictionary(kMemoryDumpConfigParam, &memory_dump_config)) - SetMemoryDumpConfigFromConfigDict(*memory_dump_config); - else - SetDefaultMemoryDumpConfig(); - } -} - -void TraceConfig::InitializeFromConfigString(StringPiece config_string) { - auto dict = DictionaryValue::From(JSONReader::Read(config_string)); - if (dict) - InitializeFromConfigDict(*dict); - else - InitializeDefault(); -} - -void TraceConfig::InitializeFromStrings(StringPiece category_filter_string, - StringPiece trace_options_string) { - if (!category_filter_string.empty()) - category_filter_.InitializeFromString(category_filter_string); - - record_mode_ = RECORD_UNTIL_FULL; - enable_systrace_ = false; - enable_argument_filter_ = false; - if (!trace_options_string.empty()) { - std::vector<std::string> split = - SplitString(trace_options_string, ",", TRIM_WHITESPACE, SPLIT_WANT_ALL); - for (const std::string& token : split) { - if (token == kRecordUntilFull) { - record_mode_ = RECORD_UNTIL_FULL; - } else if (token == kRecordContinuously) { - record_mode_ = RECORD_CONTINUOUSLY; - } else if (token == kTraceToConsole) { - record_mode_ = ECHO_TO_CONSOLE; - } else if (token == kRecordAsMuchAsPossible) { - record_mode_ = RECORD_AS_MUCH_AS_POSSIBLE; - } else if (token == kEnableSystrace) { - enable_systrace_ = true; - } else if (token == kEnableArgumentFilter) { - enable_argument_filter_ = true; - } - } - } - - if (category_filter_.IsCategoryEnabled(MemoryDumpManager::kTraceCategory)) { - SetDefaultMemoryDumpConfig(); - } -} - -void TraceConfig::SetMemoryDumpConfigFromConfigDict( - const DictionaryValue& memory_dump_config) { - // Set allowed dump modes. - memory_dump_config_.allowed_dump_modes.clear(); - const ListValue* allowed_modes_list; - if (memory_dump_config.GetList(kAllowedDumpModesParam, &allowed_modes_list)) { - for (size_t i = 0; i < allowed_modes_list->GetSize(); ++i) { - std::string level_of_detail_str; - allowed_modes_list->GetString(i, &level_of_detail_str); - memory_dump_config_.allowed_dump_modes.insert( - StringToMemoryDumpLevelOfDetail(level_of_detail_str)); - } - } else { - // If allowed modes param is not given then allow all modes by default. - memory_dump_config_.allowed_dump_modes = GetDefaultAllowedMemoryDumpModes(); - } - - // Set triggers - memory_dump_config_.triggers.clear(); - const ListValue* trigger_list = nullptr; - if (memory_dump_config.GetList(kTriggersParam, &trigger_list) && - trigger_list->GetSize() > 0) { - for (size_t i = 0; i < trigger_list->GetSize(); ++i) { - const DictionaryValue* trigger = nullptr; - if (!trigger_list->GetDictionary(i, &trigger)) - continue; - - MemoryDumpConfig::Trigger dump_config; - int interval = 0; - if (!trigger->GetInteger(kMinTimeBetweenDumps, &interval)) { - // If "min_time_between_dumps_ms" param was not given, then the trace - // config uses old format where only periodic dumps are supported. - trigger->GetInteger(kPeriodicIntervalLegacyParam, &interval); - dump_config.trigger_type = MemoryDumpType::PERIODIC_INTERVAL; - } else { - std::string trigger_type_str; - trigger->GetString(kTriggerTypeParam, &trigger_type_str); - dump_config.trigger_type = StringToMemoryDumpType(trigger_type_str); - } - DCHECK_GT(interval, 0); - dump_config.min_time_between_dumps_ms = static_cast<uint32_t>(interval); - - std::string level_of_detail_str; - trigger->GetString(kTriggerModeParam, &level_of_detail_str); - dump_config.level_of_detail = - StringToMemoryDumpLevelOfDetail(level_of_detail_str); - - memory_dump_config_.triggers.push_back(dump_config); - } - } - - // Set heap profiler options - const DictionaryValue* heap_profiler_options = nullptr; - if (memory_dump_config.GetDictionary(kHeapProfilerOptions, - &heap_profiler_options)) { - int min_size_bytes = 0; - if (heap_profiler_options->GetInteger(kBreakdownThresholdBytes, - &min_size_bytes) - && min_size_bytes >= 0) { - memory_dump_config_.heap_profiler_options.breakdown_threshold_bytes = - static_cast<size_t>(min_size_bytes); - } else { - memory_dump_config_.heap_profiler_options.breakdown_threshold_bytes = - MemoryDumpConfig::HeapProfiler::kDefaultBreakdownThresholdBytes; - } - } -} - -void TraceConfig::SetDefaultMemoryDumpConfig() { - memory_dump_config_.Clear(); - memory_dump_config_.triggers.push_back(kDefaultHeavyMemoryDumpTrigger); - memory_dump_config_.triggers.push_back(kDefaultLightMemoryDumpTrigger); - memory_dump_config_.allowed_dump_modes = GetDefaultAllowedMemoryDumpModes(); -} - -void TraceConfig::SetEventFiltersFromConfigList( - const base::ListValue& category_event_filters) { - event_filters_.clear(); - - for (size_t event_filter_index = 0; - event_filter_index < category_event_filters.GetSize(); - ++event_filter_index) { - const base::DictionaryValue* event_filter = nullptr; - if (!category_event_filters.GetDictionary(event_filter_index, - &event_filter)) - continue; - - std::string predicate_name; - CHECK(event_filter->GetString(kFilterPredicateParam, &predicate_name)) - << "Invalid predicate name in category event filter."; - - EventFilterConfig new_config(predicate_name); - new_config.InitializeFromConfigDict(event_filter); - event_filters_.push_back(new_config); - } -} - -std::unique_ptr<DictionaryValue> TraceConfig::ToDict() const { - auto dict = MakeUnique<DictionaryValue>(); - switch (record_mode_) { - case RECORD_UNTIL_FULL: - dict->SetString(kRecordModeParam, kRecordUntilFull); - break; - case RECORD_CONTINUOUSLY: - dict->SetString(kRecordModeParam, kRecordContinuously); - break; - case RECORD_AS_MUCH_AS_POSSIBLE: - dict->SetString(kRecordModeParam, kRecordAsMuchAsPossible); - break; - case ECHO_TO_CONSOLE: - dict->SetString(kRecordModeParam, kTraceToConsole); - break; - default: - NOTREACHED(); - } - - dict->SetBoolean(kEnableSystraceParam, enable_systrace_); - dict->SetBoolean(kEnableArgumentFilterParam, enable_argument_filter_); - - category_filter_.ToDict(dict.get()); - - if (!event_filters_.empty()) { - std::unique_ptr<base::ListValue> filter_list(new base::ListValue()); - for (const EventFilterConfig& filter : event_filters_) { - std::unique_ptr<base::DictionaryValue> filter_dict( - new base::DictionaryValue()); - filter.ToDict(filter_dict.get()); - filter_list->Append(std::move(filter_dict)); - } - dict->Set(kEventFiltersParam, std::move(filter_list)); - } - - if (category_filter_.IsCategoryEnabled(MemoryDumpManager::kTraceCategory)) { - auto allowed_modes = MakeUnique<ListValue>(); - for (auto dump_mode : memory_dump_config_.allowed_dump_modes) - allowed_modes->AppendString(MemoryDumpLevelOfDetailToString(dump_mode)); - - auto memory_dump_config = MakeUnique<DictionaryValue>(); - memory_dump_config->Set(kAllowedDumpModesParam, std::move(allowed_modes)); - - auto triggers_list = MakeUnique<ListValue>(); - for (const auto& config : memory_dump_config_.triggers) { - auto trigger_dict = MakeUnique<DictionaryValue>(); - trigger_dict->SetString(kTriggerTypeParam, - MemoryDumpTypeToString(config.trigger_type)); - trigger_dict->SetInteger( - kMinTimeBetweenDumps, - static_cast<int>(config.min_time_between_dumps_ms)); - trigger_dict->SetString( - kTriggerModeParam, - MemoryDumpLevelOfDetailToString(config.level_of_detail)); - triggers_list->Append(std::move(trigger_dict)); - } - - // Empty triggers will still be specified explicitly since it means that - // the periodic dumps are not enabled. - memory_dump_config->Set(kTriggersParam, std::move(triggers_list)); - - if (memory_dump_config_.heap_profiler_options.breakdown_threshold_bytes != - MemoryDumpConfig::HeapProfiler::kDefaultBreakdownThresholdBytes) { - auto options = MakeUnique<DictionaryValue>(); - options->SetInteger( - kBreakdownThresholdBytes, - memory_dump_config_.heap_profiler_options.breakdown_threshold_bytes); - memory_dump_config->Set(kHeapProfilerOptions, std::move(options)); - } - dict->Set(kMemoryDumpConfigParam, std::move(memory_dump_config)); - } - return dict; -} - -std::string TraceConfig::ToTraceOptionsString() const { - std::string ret; - switch (record_mode_) { - case RECORD_UNTIL_FULL: - ret = kRecordUntilFull; - break; - case RECORD_CONTINUOUSLY: - ret = kRecordContinuously; - break; - case RECORD_AS_MUCH_AS_POSSIBLE: - ret = kRecordAsMuchAsPossible; - break; - case ECHO_TO_CONSOLE: - ret = kTraceToConsole; - break; - default: - NOTREACHED(); - } - if (enable_systrace_) - ret = ret + "," + kEnableSystrace; - if (enable_argument_filter_) - ret = ret + "," + kEnableArgumentFilter; - return ret; -} - -} // namespace trace_event -} // namespace base diff --git a/base/trace_event/trace_config.h b/base/trace_event/trace_config.h deleted file mode 100644 index 13b2f5f0ee..0000000000 --- a/base/trace_event/trace_config.h +++ /dev/null @@ -1,300 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_TRACE_EVENT_TRACE_CONFIG_H_ -#define BASE_TRACE_EVENT_TRACE_CONFIG_H_ - -#include <stdint.h> - -#include <memory> -#include <set> -#include <string> -#include <unordered_set> -#include <vector> - -#include "base/base_export.h" -#include "base/gtest_prod_util.h" -#include "base/strings/string_piece.h" -#include "base/trace_event/memory_dump_request_args.h" -#include "base/trace_event/trace_config_category_filter.h" -#include "base/values.h" - -namespace base { -namespace trace_event { - -class ConvertableToTraceFormat; - -// Options determines how the trace buffer stores data. -enum TraceRecordMode { - // Record until the trace buffer is full. - RECORD_UNTIL_FULL, - - // Record until the user ends the trace. The trace buffer is a fixed size - // and we use it as a ring buffer during recording. - RECORD_CONTINUOUSLY, - - // Record until the trace buffer is full, but with a huge buffer size. - RECORD_AS_MUCH_AS_POSSIBLE, - - // Echo to console. Events are discarded. - ECHO_TO_CONSOLE, -}; - -class BASE_EXPORT TraceConfig { - public: - using StringList = std::vector<std::string>; - - // Specifies the memory dump config for tracing. - // Used only when "memory-infra" category is enabled. - struct BASE_EXPORT MemoryDumpConfig { - MemoryDumpConfig(); - MemoryDumpConfig(const MemoryDumpConfig& other); - ~MemoryDumpConfig(); - - // Specifies the triggers in the memory dump config. - struct Trigger { - uint32_t min_time_between_dumps_ms; - MemoryDumpLevelOfDetail level_of_detail; - MemoryDumpType trigger_type; - }; - - // Specifies the configuration options for the heap profiler. - struct HeapProfiler { - // Default value for |breakdown_threshold_bytes|. - enum { kDefaultBreakdownThresholdBytes = 1024 }; - - HeapProfiler(); - - // Reset the options to default. - void Clear(); - - uint32_t breakdown_threshold_bytes; - }; - - // Reset the values in the config. - void Clear(); - - void Merge(const MemoryDumpConfig& config); - - // Set of memory dump modes allowed for the tracing session. The explicitly - // triggered dumps will be successful only if the dump mode is allowed in - // the config. - std::set<MemoryDumpLevelOfDetail> allowed_dump_modes; - - std::vector<Trigger> triggers; - HeapProfiler heap_profiler_options; - }; - - class BASE_EXPORT EventFilterConfig { - public: - EventFilterConfig(const std::string& predicate_name); - EventFilterConfig(const EventFilterConfig& tc); - - ~EventFilterConfig(); - - EventFilterConfig& operator=(const EventFilterConfig& rhs); - - void InitializeFromConfigDict(const base::DictionaryValue* event_filter); - - void SetCategoryFilter(const TraceConfigCategoryFilter& category_filter); - - void ToDict(DictionaryValue* filter_dict) const; - - bool GetArgAsSet(const char* key, std::unordered_set<std::string>*) const; - - bool IsCategoryGroupEnabled(const StringPiece& category_group_name) const; - - const std::string& predicate_name() const { return predicate_name_; } - base::DictionaryValue* filter_args() const { return args_.get(); } - const TraceConfigCategoryFilter& category_filter() const { - return category_filter_; - } - - private: - std::string predicate_name_; - TraceConfigCategoryFilter category_filter_; - std::unique_ptr<base::DictionaryValue> args_; - }; - typedef std::vector<EventFilterConfig> EventFilters; - - TraceConfig(); - - // Create TraceConfig object from category filter and trace options strings. - // - // |category_filter_string| is a comma-delimited list of category wildcards. - // A category can have an optional '-' prefix to make it an excluded category. - // All the same rules apply above, so for example, having both included and - // excluded categories in the same list would not be supported. - // - // Category filters can also be used to configure synthetic delays. - // - // |trace_options_string| is a comma-delimited list of trace options. - // Possible options are: "record-until-full", "record-continuously", - // "record-as-much-as-possible", "trace-to-console", "enable-systrace" and - // "enable-argument-filter". - // The first 4 options are trace recoding modes and hence - // mutually exclusive. If more than one trace recording modes appear in the - // options_string, the last one takes precedence. If none of the trace - // recording mode is specified, recording mode is RECORD_UNTIL_FULL. - // - // The trace option will first be reset to the default option - // (record_mode set to RECORD_UNTIL_FULL, enable_systrace and - // enable_argument_filter set to false) before options parsed from - // |trace_options_string| are applied on it. If |trace_options_string| is - // invalid, the final state of trace options is undefined. - // - // Example: TraceConfig("test_MyTest*", "record-until-full"); - // Example: TraceConfig("test_MyTest*,test_OtherStuff", - // "record-continuously"); - // Example: TraceConfig("-excluded_category1,-excluded_category2", - // "record-until-full, trace-to-console"); - // would set ECHO_TO_CONSOLE as the recording mode. - // Example: TraceConfig("-*,webkit", ""); - // would disable everything but webkit; and use default options. - // Example: TraceConfig("-webkit", ""); - // would enable everything but webkit; and use default options. - // Example: TraceConfig("DELAY(gpu.PresentingFrame;16)", ""); - // would make swap buffers always take at least 16 ms; and use - // default options. - // Example: TraceConfig("DELAY(gpu.PresentingFrame;16;oneshot)", ""); - // would make swap buffers take at least 16 ms the first time it is - // called; and use default options. - // Example: TraceConfig("DELAY(gpu.PresentingFrame;16;alternating)", ""); - // would make swap buffers take at least 16 ms every other time it - // is called; and use default options. - TraceConfig(StringPiece category_filter_string, - StringPiece trace_options_string); - - TraceConfig(StringPiece category_filter_string, TraceRecordMode record_mode); - - // Create TraceConfig object from the trace config string. - // - // |config_string| is a dictionary formatted as a JSON string, containing both - // category filters and trace options. - // - // Example: - // { - // "record_mode": "record-continuously", - // "enable_systrace": true, - // "enable_argument_filter": true, - // "included_categories": ["included", - // "inc_pattern*", - // "disabled-by-default-memory-infra"], - // "excluded_categories": ["excluded", "exc_pattern*"], - // "synthetic_delays": ["test.Delay1;16", "test.Delay2;32"], - // "memory_dump_config": { - // "triggers": [ - // { - // "mode": "detailed", - // "periodic_interval_ms": 2000 - // } - // ] - // } - // } - // - // Note: memory_dump_config can be specified only if - // disabled-by-default-memory-infra category is enabled. - explicit TraceConfig(StringPiece config_string); - - // Functionally identical to the above, but takes a parsed dictionary as input - // instead of its JSON serialization. - explicit TraceConfig(const DictionaryValue& config); - - TraceConfig(const TraceConfig& tc); - - ~TraceConfig(); - - TraceConfig& operator=(const TraceConfig& rhs); - - // Return a list of the synthetic delays specified in this category filter. - const StringList& GetSyntheticDelayValues() const; - - TraceRecordMode GetTraceRecordMode() const { return record_mode_; } - bool IsSystraceEnabled() const { return enable_systrace_; } - bool IsArgumentFilterEnabled() const { return enable_argument_filter_; } - - void SetTraceRecordMode(TraceRecordMode mode) { record_mode_ = mode; } - void EnableSystrace() { enable_systrace_ = true; } - void EnableArgumentFilter() { enable_argument_filter_ = true; } - - // Writes the string representation of the TraceConfig. The string is JSON - // formatted. - std::string ToString() const; - - // Returns a copy of the TraceConfig wrapped in a ConvertableToTraceFormat - std::unique_ptr<ConvertableToTraceFormat> AsConvertableToTraceFormat() const; - - // Write the string representation of the CategoryFilter part. - std::string ToCategoryFilterString() const; - - // Returns true if at least one category in the list is enabled by this - // trace config. This is used to determine if the category filters are - // enabled in the TRACE_* macros. - bool IsCategoryGroupEnabled(const StringPiece& category_group_name) const; - - // Merges config with the current TraceConfig - void Merge(const TraceConfig& config); - - void Clear(); - - // Clears and resets the memory dump config. - void ResetMemoryDumpConfig(const MemoryDumpConfig& memory_dump_config); - - const TraceConfigCategoryFilter& category_filter() const { - return category_filter_; - } - - const MemoryDumpConfig& memory_dump_config() const { - return memory_dump_config_; - } - - const EventFilters& event_filters() const { return event_filters_; } - void SetEventFilters(const EventFilters& filter_configs) { - event_filters_ = filter_configs; - } - - private: - FRIEND_TEST_ALL_PREFIXES(TraceConfigTest, TraceConfigFromValidLegacyFormat); - FRIEND_TEST_ALL_PREFIXES(TraceConfigTest, - TraceConfigFromInvalidLegacyStrings); - - // The default trace config, used when none is provided. - // Allows all non-disabled-by-default categories through, except if they end - // in the suffix 'Debug' or 'Test'. - void InitializeDefault(); - - // Initialize from a config dictionary. - void InitializeFromConfigDict(const DictionaryValue& dict); - - // Initialize from a config string. - void InitializeFromConfigString(StringPiece config_string); - - // Initialize from category filter and trace options strings - void InitializeFromStrings(StringPiece category_filter_string, - StringPiece trace_options_string); - - void SetMemoryDumpConfigFromConfigDict( - const DictionaryValue& memory_dump_config); - void SetDefaultMemoryDumpConfig(); - - void SetEventFiltersFromConfigList(const base::ListValue& event_filters); - std::unique_ptr<DictionaryValue> ToDict() const; - - std::string ToTraceOptionsString() const; - - TraceRecordMode record_mode_; - bool enable_systrace_ : 1; - bool enable_argument_filter_ : 1; - - TraceConfigCategoryFilter category_filter_; - - MemoryDumpConfig memory_dump_config_; - - EventFilters event_filters_; -}; - -} // namespace trace_event -} // namespace base - -#endif // BASE_TRACE_EVENT_TRACE_CONFIG_H_ diff --git a/base/trace_event/trace_config_category_filter.cc b/base/trace_event/trace_config_category_filter.cc deleted file mode 100644 index 234db18c5c..0000000000 --- a/base/trace_event/trace_config_category_filter.cc +++ /dev/null @@ -1,297 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/trace_event/trace_config_category_filter.h" - -#include "base/memory/ptr_util.h" -#include "base/strings/pattern.h" -#include "base/strings/string_split.h" -#include "base/strings/string_tokenizer.h" -#include "base/strings/string_util.h" -#include "base/strings/stringprintf.h" -#include "base/trace_event/trace_event.h" - -namespace base { -namespace trace_event { - -namespace { -const char kIncludedCategoriesParam[] = "included_categories"; -const char kExcludedCategoriesParam[] = "excluded_categories"; -const char kSyntheticDelaysParam[] = "synthetic_delays"; - -const char kSyntheticDelayCategoryFilterPrefix[] = "DELAY("; -} - -TraceConfigCategoryFilter::TraceConfigCategoryFilter() {} - -TraceConfigCategoryFilter::TraceConfigCategoryFilter( - const TraceConfigCategoryFilter& other) - : included_categories_(other.included_categories_), - disabled_categories_(other.disabled_categories_), - excluded_categories_(other.excluded_categories_), - synthetic_delays_(other.synthetic_delays_) {} - -TraceConfigCategoryFilter::~TraceConfigCategoryFilter() {} - -TraceConfigCategoryFilter& TraceConfigCategoryFilter::operator=( - const TraceConfigCategoryFilter& rhs) { - included_categories_ = rhs.included_categories_; - disabled_categories_ = rhs.disabled_categories_; - excluded_categories_ = rhs.excluded_categories_; - synthetic_delays_ = rhs.synthetic_delays_; - return *this; -} - -void TraceConfigCategoryFilter::InitializeFromString( - const StringPiece& category_filter_string) { - std::vector<StringPiece> split = SplitStringPiece( - category_filter_string, ",", TRIM_WHITESPACE, SPLIT_WANT_ALL); - for (const StringPiece& category : split) { - // Ignore empty categories. - if (category.empty()) - continue; - // Synthetic delays are of the form 'DELAY(delay;option;option;...)'. - if (StartsWith(category, kSyntheticDelayCategoryFilterPrefix, - CompareCase::SENSITIVE) && - category.back() == ')') { - StringPiece synthetic_category = category.substr( - strlen(kSyntheticDelayCategoryFilterPrefix), - category.size() - strlen(kSyntheticDelayCategoryFilterPrefix) - 1); - size_t name_length = synthetic_category.find(';'); - if (name_length != std::string::npos && name_length > 0 && - name_length != synthetic_category.size() - 1) { - synthetic_delays_.push_back(synthetic_category.as_string()); - } - } else if (category.front() == '-') { - // Excluded categories start with '-'. - // Remove '-' from category string. - excluded_categories_.push_back(category.substr(1).as_string()); - } else if (category.starts_with(TRACE_DISABLED_BY_DEFAULT(""))) { - disabled_categories_.push_back(category.as_string()); - } else { - included_categories_.push_back(category.as_string()); - } - } -} - -void TraceConfigCategoryFilter::InitializeFromConfigDict( - const DictionaryValue& dict) { - const ListValue* category_list = nullptr; - if (dict.GetList(kIncludedCategoriesParam, &category_list)) - SetCategoriesFromIncludedList(*category_list); - if (dict.GetList(kExcludedCategoriesParam, &category_list)) - SetCategoriesFromExcludedList(*category_list); - if (dict.GetList(kSyntheticDelaysParam, &category_list)) - SetSyntheticDelaysFromList(*category_list); -} - -bool TraceConfigCategoryFilter::IsCategoryGroupEnabled( - const StringPiece& category_group_name) const { - bool had_enabled_by_default = false; - DCHECK(!category_group_name.empty()); - CStringTokenizer category_group_tokens(category_group_name.begin(), - category_group_name.end(), ","); - while (category_group_tokens.GetNext()) { - StringPiece category_group_token = category_group_tokens.token_piece(); - // Don't allow empty tokens, nor tokens with leading or trailing space. - DCHECK(IsCategoryNameAllowed(category_group_token)) - << "Disallowed category string"; - if (IsCategoryEnabled(category_group_token)) - return true; - - if (!MatchPattern(category_group_token, TRACE_DISABLED_BY_DEFAULT("*"))) - had_enabled_by_default = true; - } - // Do a second pass to check for explicitly disabled categories - // (those explicitly enabled have priority due to first pass). - category_group_tokens.Reset(); - bool category_group_disabled = false; - while (category_group_tokens.GetNext()) { - StringPiece category_group_token = category_group_tokens.token_piece(); - for (const std::string& category : excluded_categories_) { - if (MatchPattern(category_group_token, category)) { - // Current token of category_group_name is present in excluded_list. - // Flag the exclusion and proceed further to check if any of the - // remaining categories of category_group_name is not present in the - // excluded_ list. - category_group_disabled = true; - break; - } - // One of the category of category_group_name is not present in - // excluded_ list. So, if it's not a disabled-by-default category, - // it has to be included_ list. Enable the category_group_name - // for recording. - if (!MatchPattern(category_group_token, TRACE_DISABLED_BY_DEFAULT("*"))) - category_group_disabled = false; - } - // One of the categories present in category_group_name is not present in - // excluded_ list. Implies this category_group_name group can be enabled - // for recording, since one of its groups is enabled for recording. - if (!category_group_disabled) - break; - } - // If the category group is not excluded, and there are no included patterns - // we consider this category group enabled, as long as it had categories - // other than disabled-by-default. - return !category_group_disabled && had_enabled_by_default && - included_categories_.empty(); -} - -bool TraceConfigCategoryFilter::IsCategoryEnabled( - const StringPiece& category_name) const { - // Check the disabled- filters and the disabled-* wildcard first so that a - // "*" filter does not include the disabled. - for (const std::string& category : disabled_categories_) { - if (MatchPattern(category_name, category)) - return true; - } - - if (MatchPattern(category_name, TRACE_DISABLED_BY_DEFAULT("*"))) - return false; - - for (const std::string& category : included_categories_) { - if (MatchPattern(category_name, category)) - return true; - } - - return false; -} - -void TraceConfigCategoryFilter::Merge(const TraceConfigCategoryFilter& config) { - // Keep included patterns only if both filters have an included entry. - // Otherwise, one of the filter was specifying "*" and we want to honor the - // broadest filter. - if (!included_categories_.empty() && !config.included_categories_.empty()) { - included_categories_.insert(included_categories_.end(), - config.included_categories_.begin(), - config.included_categories_.end()); - } else { - included_categories_.clear(); - } - - disabled_categories_.insert(disabled_categories_.end(), - config.disabled_categories_.begin(), - config.disabled_categories_.end()); - excluded_categories_.insert(excluded_categories_.end(), - config.excluded_categories_.begin(), - config.excluded_categories_.end()); - synthetic_delays_.insert(synthetic_delays_.end(), - config.synthetic_delays_.begin(), - config.synthetic_delays_.end()); -} - -void TraceConfigCategoryFilter::Clear() { - included_categories_.clear(); - disabled_categories_.clear(); - excluded_categories_.clear(); - synthetic_delays_.clear(); -} - -void TraceConfigCategoryFilter::ToDict(DictionaryValue* dict) const { - StringList categories(included_categories_); - categories.insert(categories.end(), disabled_categories_.begin(), - disabled_categories_.end()); - AddCategoriesToDict(categories, kIncludedCategoriesParam, dict); - AddCategoriesToDict(excluded_categories_, kExcludedCategoriesParam, dict); - AddCategoriesToDict(synthetic_delays_, kSyntheticDelaysParam, dict); -} - -std::string TraceConfigCategoryFilter::ToFilterString() const { - std::string filter_string; - WriteCategoryFilterString(included_categories_, &filter_string, true); - WriteCategoryFilterString(disabled_categories_, &filter_string, true); - WriteCategoryFilterString(excluded_categories_, &filter_string, false); - WriteCategoryFilterString(synthetic_delays_, &filter_string); - return filter_string; -} - -void TraceConfigCategoryFilter::SetCategoriesFromIncludedList( - const ListValue& included_list) { - included_categories_.clear(); - for (size_t i = 0; i < included_list.GetSize(); ++i) { - std::string category; - if (!included_list.GetString(i, &category)) - continue; - if (category.compare(0, strlen(TRACE_DISABLED_BY_DEFAULT("")), - TRACE_DISABLED_BY_DEFAULT("")) == 0) { - disabled_categories_.push_back(category); - } else { - included_categories_.push_back(category); - } - } -} - -void TraceConfigCategoryFilter::SetCategoriesFromExcludedList( - const ListValue& excluded_list) { - excluded_categories_.clear(); - for (size_t i = 0; i < excluded_list.GetSize(); ++i) { - std::string category; - if (excluded_list.GetString(i, &category)) - excluded_categories_.push_back(category); - } -} - -void TraceConfigCategoryFilter::SetSyntheticDelaysFromList( - const ListValue& list) { - for (size_t i = 0; i < list.GetSize(); ++i) { - std::string delay; - if (!list.GetString(i, &delay)) - continue; - // Synthetic delays are of the form "delay;option;option;...". - size_t name_length = delay.find(';'); - if (name_length != std::string::npos && name_length > 0 && - name_length != delay.size() - 1) { - synthetic_delays_.push_back(delay); - } - } -} - -void TraceConfigCategoryFilter::AddCategoriesToDict( - const StringList& categories, - const char* param, - DictionaryValue* dict) const { - if (categories.empty()) - return; - - auto list = MakeUnique<ListValue>(); - for (const std::string& category : categories) - list->AppendString(category); - dict->Set(param, std::move(list)); -} - -void TraceConfigCategoryFilter::WriteCategoryFilterString( - const StringList& values, - std::string* out, - bool included) const { - bool prepend_comma = !out->empty(); - int token_cnt = 0; - for (const std::string& category : values) { - if (token_cnt > 0 || prepend_comma) - StringAppendF(out, ","); - StringAppendF(out, "%s%s", (included ? "" : "-"), category.c_str()); - ++token_cnt; - } -} - -void TraceConfigCategoryFilter::WriteCategoryFilterString( - const StringList& delays, - std::string* out) const { - bool prepend_comma = !out->empty(); - int token_cnt = 0; - for (const std::string& category : delays) { - if (token_cnt > 0 || prepend_comma) - StringAppendF(out, ","); - StringAppendF(out, "%s%s)", kSyntheticDelayCategoryFilterPrefix, - category.c_str()); - ++token_cnt; - } -} - -// static -bool TraceConfigCategoryFilter::IsCategoryNameAllowed(StringPiece str) { - return !str.empty() && str.front() != ' ' && str.back() != ' '; -} - -} // namespace trace_event -} // namespace base diff --git a/base/trace_event/trace_config_category_filter.h b/base/trace_event/trace_config_category_filter.h deleted file mode 100644 index 0d7dba0374..0000000000 --- a/base/trace_event/trace_config_category_filter.h +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_TRACE_EVENT_TRACE_CONFIG_CATEGORY_FILTER_H_ -#define BASE_TRACE_EVENT_TRACE_CONFIG_CATEGORY_FILTER_H_ - -#include <string> -#include <vector> - -#include "base/base_export.h" -#include "base/strings/string_piece.h" -#include "base/values.h" - -namespace base { -namespace trace_event { - -// Configuration of categories enabled and disabled in TraceConfig. -class BASE_EXPORT TraceConfigCategoryFilter { - public: - using StringList = std::vector<std::string>; - - TraceConfigCategoryFilter(); - TraceConfigCategoryFilter(const TraceConfigCategoryFilter& other); - ~TraceConfigCategoryFilter(); - - TraceConfigCategoryFilter& operator=(const TraceConfigCategoryFilter& rhs); - - // Initializes from category filter string. See TraceConfig constructor for - // description of how to write category filter string. - void InitializeFromString(const StringPiece& category_filter_string); - - // Initializes TraceConfigCategoryFilter object from the config dictionary. - void InitializeFromConfigDict(const DictionaryValue& dict); - - // Merges this with category filter config. - void Merge(const TraceConfigCategoryFilter& config); - void Clear(); - - // Returns true if at least one category in the list is enabled by this - // trace config. This is used to determine if the category filters are - // enabled in the TRACE_* macros. - bool IsCategoryGroupEnabled(const StringPiece& category_group_name) const; - - // Returns true if the category is enabled according to this trace config. - // This tells whether a category is enabled from the TraceConfig's - // perspective. Please refer to IsCategoryGroupEnabled() to determine if a - // category is enabled from the tracing runtime's perspective. - bool IsCategoryEnabled(const StringPiece& category_name) const; - - void ToDict(DictionaryValue* dict) const; - - std::string ToFilterString() const; - - // Returns true if category name is a valid string. - static bool IsCategoryNameAllowed(StringPiece str); - - const StringList& included_categories() const { return included_categories_; } - const StringList& excluded_categories() const { return excluded_categories_; } - const StringList& synthetic_delays() const { return synthetic_delays_; } - - private: - void SetCategoriesFromIncludedList(const ListValue& included_list); - void SetCategoriesFromExcludedList(const ListValue& excluded_list); - void SetSyntheticDelaysFromList(const ListValue& list); - - void AddCategoriesToDict(const StringList& categories, - const char* param, - DictionaryValue* dict) const; - - void WriteCategoryFilterString(const StringList& values, - std::string* out, - bool included) const; - void WriteCategoryFilterString(const StringList& delays, - std::string* out) const; - - StringList included_categories_; - StringList disabled_categories_; - StringList excluded_categories_; - StringList synthetic_delays_; -}; - -} // namespace trace_event -} // namespace base - -#endif // BASE_TRACE_EVENT_TRACE_CONFIG_CATEGORY_FILTER_H_ diff --git a/base/trace_event/trace_config_memory_test_util.h b/base/trace_event/trace_config_memory_test_util.h deleted file mode 100644 index 744e8a8acc..0000000000 --- a/base/trace_event/trace_config_memory_test_util.h +++ /dev/null @@ -1,160 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_TRACE_EVENT_TRACE_CONFIG_MEMORY_TEST_UTIL_H_ -#define BASE_TRACE_EVENT_TRACE_CONFIG_MEMORY_TEST_UTIL_H_ - -#include "base/strings/stringprintf.h" -#include "base/trace_event/memory_dump_manager.h" - -namespace base { -namespace trace_event { - -class TraceConfigMemoryTestUtil { - public: - static std::string GetTraceConfig_LegacyPeriodicTriggers(int light_period, - int heavy_period) { - return StringPrintf( - "{" - "\"enable_argument_filter\":false," - "\"enable_systrace\":false," - "\"included_categories\":[" - "\"%s\"" - "]," - "\"memory_dump_config\":{" - "\"allowed_dump_modes\":[\"background\",\"light\",\"detailed\"]," - "\"heap_profiler_options\":{" - "\"breakdown_threshold_bytes\":2048" - "}," - "\"triggers\":[" - "{" - "\"mode\":\"light\"," - "\"periodic_interval_ms\":%d" - "}," - "{" - "\"mode\":\"detailed\"," - "\"periodic_interval_ms\":%d" - "}" - "]" - "}," - "\"record_mode\":\"record-until-full\"" - "}", - MemoryDumpManager::kTraceCategory, light_period, heavy_period); - ; - } - - static std::string GetTraceConfig_PeriodicTriggers(int light_period, - int heavy_period) { - return StringPrintf( - "{" - "\"enable_argument_filter\":false," - "\"enable_systrace\":false," - "\"included_categories\":[" - "\"%s\"" - "]," - "\"memory_dump_config\":{" - "\"allowed_dump_modes\":[\"background\",\"light\",\"detailed\"]," - "\"heap_profiler_options\":{" - "\"breakdown_threshold_bytes\":2048" - "}," - "\"triggers\":[" - "{" - "\"min_time_between_dumps_ms\":%d," - "\"mode\":\"light\"," - "\"type\":\"periodic_interval\"" - "}," - "{" - "\"min_time_between_dumps_ms\":%d," - "\"mode\":\"detailed\"," - "\"type\":\"periodic_interval\"" - "}" - "]" - "}," - "\"record_mode\":\"record-until-full\"" - "}", - MemoryDumpManager::kTraceCategory, light_period, heavy_period); - } - - static std::string GetTraceConfig_EmptyTriggers() { - return StringPrintf( - "{" - "\"enable_argument_filter\":false," - "\"enable_systrace\":false," - "\"included_categories\":[" - "\"%s\"" - "]," - "\"memory_dump_config\":{" - "\"allowed_dump_modes\":[\"background\",\"light\",\"detailed\"]," - "\"triggers\":[" - "]" - "}," - "\"record_mode\":\"record-until-full\"" - "}", - MemoryDumpManager::kTraceCategory); - } - - static std::string GetTraceConfig_NoTriggers() { - return StringPrintf( - "{" - "\"enable_argument_filter\":false," - "\"enable_systrace\":false," - "\"included_categories\":[" - "\"%s\"" - "]," - "\"record_mode\":\"record-until-full\"" - "}", - MemoryDumpManager::kTraceCategory); - } - - static std::string GetTraceConfig_BackgroundTrigger(int period_ms) { - return StringPrintf( - "{" - "\"enable_argument_filter\":false," - "\"enable_systrace\":false," - "\"included_categories\":[" - "\"%s\"" - "]," - "\"memory_dump_config\":{" - "\"allowed_dump_modes\":[\"background\"]," - "\"triggers\":[" - "{" - "\"min_time_between_dumps_ms\":%d," - "\"mode\":\"background\"," - "\"type\":\"periodic_interval\"" - "}" - "]" - "}," - "\"record_mode\":\"record-until-full\"" - "}", - MemoryDumpManager::kTraceCategory, period_ms); - } - - static std::string GetTraceConfig_PeakDetectionTrigger(int heavy_period) { - return StringPrintf( - "{" - "\"enable_argument_filter\":false," - "\"enable_systrace\":false," - "\"included_categories\":[" - "\"%s\"" - "]," - "\"memory_dump_config\":{" - "\"allowed_dump_modes\":[\"background\",\"light\",\"detailed\"]," - "\"triggers\":[" - "{" - "\"min_time_between_dumps_ms\":%d," - "\"mode\":\"detailed\"," - "\"type\":\"peak_memory_usage\"" - "}" - "]" - "}," - "\"record_mode\":\"record-until-full\"" - "}", - MemoryDumpManager::kTraceCategory, heavy_period); - } -}; - -} // namespace trace_event -} // namespace base - -#endif // BASE_TRACE_EVENT_TRACE_CONFIG_MEMORY_TEST_UTIL_H_ diff --git a/base/trace_event/trace_config_unittest.cc b/base/trace_event/trace_config_unittest.cc deleted file mode 100644 index a856c27192..0000000000 --- a/base/trace_event/trace_config_unittest.cc +++ /dev/null @@ -1,708 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include <stddef.h> - -#include "base/json/json_reader.h" -#include "base/json/json_writer.h" -#include "base/macros.h" -#include "base/trace_event/memory_dump_manager.h" -#include "base/trace_event/trace_config.h" -#include "base/trace_event/trace_config_memory_test_util.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { -namespace trace_event { - -namespace { - -const char kDefaultTraceConfigString[] = - "{" - "\"enable_argument_filter\":false," - "\"enable_systrace\":false," - "\"record_mode\":\"record-until-full\"" - "}"; - -const char kCustomTraceConfigString[] = - "{" - "\"enable_argument_filter\":true," - "\"enable_systrace\":true," - "\"event_filters\":[" - "{" - "\"excluded_categories\":[\"unfiltered_cat\"]," - "\"filter_args\":{\"event_name_whitelist\":[\"a snake\",\"a dog\"]}," - "\"filter_predicate\":\"event_whitelist_predicate\"," - "\"included_categories\":[\"*\"]" - "}" - "]," - "\"excluded_categories\":[\"excluded\",\"exc_pattern*\"]," - "\"included_categories\":[" - "\"included\"," - "\"inc_pattern*\"," - "\"disabled-by-default-cc\"," - "\"disabled-by-default-memory-infra\"]," - "\"memory_dump_config\":{" - "\"allowed_dump_modes\":[\"background\",\"light\",\"detailed\"]," - "\"heap_profiler_options\":{" - "\"breakdown_threshold_bytes\":10240" - "}," - "\"triggers\":[" - "{" - "\"min_time_between_dumps_ms\":50," - "\"mode\":\"light\"," - "\"type\":\"periodic_interval\"" - "}," - "{" - "\"min_time_between_dumps_ms\":1000," - "\"mode\":\"detailed\"," - "\"type\":\"peak_memory_usage\"" - "}" - "]" - "}," - "\"record_mode\":\"record-continuously\"," - "\"synthetic_delays\":[\"test.Delay1;16\",\"test.Delay2;32\"]" - "}"; - -void CheckDefaultTraceConfigBehavior(const TraceConfig& tc) { - EXPECT_EQ(RECORD_UNTIL_FULL, tc.GetTraceRecordMode()); - EXPECT_FALSE(tc.IsSystraceEnabled()); - EXPECT_FALSE(tc.IsArgumentFilterEnabled()); - - // Default trace config enables every category filter except the - // disabled-by-default-* ones. - EXPECT_TRUE(tc.IsCategoryGroupEnabled("Category1")); - EXPECT_TRUE(tc.IsCategoryGroupEnabled("not-excluded-category")); - EXPECT_FALSE(tc.IsCategoryGroupEnabled("disabled-by-default-cc")); - - EXPECT_TRUE(tc.IsCategoryGroupEnabled("Category1,not-excluded-category")); - EXPECT_TRUE(tc.IsCategoryGroupEnabled("Category1,disabled-by-default-cc")); - EXPECT_FALSE(tc.IsCategoryGroupEnabled( - "disabled-by-default-cc,disabled-by-default-cc2")); -} - -} // namespace - -TEST(TraceConfigTest, TraceConfigFromValidLegacyFormat) { - // From trace options strings - TraceConfig config("", "record-until-full"); - EXPECT_EQ(RECORD_UNTIL_FULL, config.GetTraceRecordMode()); - EXPECT_FALSE(config.IsSystraceEnabled()); - EXPECT_FALSE(config.IsArgumentFilterEnabled()); - EXPECT_STREQ("record-until-full", config.ToTraceOptionsString().c_str()); - - config = TraceConfig("", "record-continuously"); - EXPECT_EQ(RECORD_CONTINUOUSLY, config.GetTraceRecordMode()); - EXPECT_FALSE(config.IsSystraceEnabled()); - EXPECT_FALSE(config.IsArgumentFilterEnabled()); - EXPECT_STREQ("record-continuously", config.ToTraceOptionsString().c_str()); - - config = TraceConfig("", "trace-to-console"); - EXPECT_EQ(ECHO_TO_CONSOLE, config.GetTraceRecordMode()); - EXPECT_FALSE(config.IsSystraceEnabled()); - EXPECT_FALSE(config.IsArgumentFilterEnabled()); - EXPECT_STREQ("trace-to-console", config.ToTraceOptionsString().c_str()); - - config = TraceConfig("", "record-as-much-as-possible"); - EXPECT_EQ(RECORD_AS_MUCH_AS_POSSIBLE, config.GetTraceRecordMode()); - EXPECT_FALSE(config.IsSystraceEnabled()); - EXPECT_FALSE(config.IsArgumentFilterEnabled()); - EXPECT_STREQ("record-as-much-as-possible", - config.ToTraceOptionsString().c_str()); - - config = TraceConfig("", "enable-systrace, record-continuously"); - EXPECT_EQ(RECORD_CONTINUOUSLY, config.GetTraceRecordMode()); - EXPECT_TRUE(config.IsSystraceEnabled()); - EXPECT_FALSE(config.IsArgumentFilterEnabled()); - EXPECT_STREQ("record-continuously,enable-systrace", - config.ToTraceOptionsString().c_str()); - - config = TraceConfig("", "enable-argument-filter,record-as-much-as-possible"); - EXPECT_EQ(RECORD_AS_MUCH_AS_POSSIBLE, config.GetTraceRecordMode()); - EXPECT_FALSE(config.IsSystraceEnabled()); - EXPECT_TRUE(config.IsArgumentFilterEnabled()); - EXPECT_STREQ("record-as-much-as-possible,enable-argument-filter", - config.ToTraceOptionsString().c_str()); - - config = TraceConfig( - "", - "enable-systrace,trace-to-console,enable-argument-filter"); - EXPECT_EQ(ECHO_TO_CONSOLE, config.GetTraceRecordMode()); - EXPECT_TRUE(config.IsSystraceEnabled()); - EXPECT_TRUE(config.IsArgumentFilterEnabled()); - EXPECT_STREQ( - "trace-to-console,enable-systrace,enable-argument-filter", - config.ToTraceOptionsString().c_str()); - - config = TraceConfig( - "", "record-continuously, record-until-full, trace-to-console"); - EXPECT_EQ(ECHO_TO_CONSOLE, config.GetTraceRecordMode()); - EXPECT_FALSE(config.IsSystraceEnabled()); - EXPECT_FALSE(config.IsArgumentFilterEnabled()); - EXPECT_STREQ("trace-to-console", config.ToTraceOptionsString().c_str()); - - // From TraceRecordMode - config = TraceConfig("", RECORD_UNTIL_FULL); - EXPECT_EQ(RECORD_UNTIL_FULL, config.GetTraceRecordMode()); - EXPECT_FALSE(config.IsSystraceEnabled()); - EXPECT_FALSE(config.IsArgumentFilterEnabled()); - EXPECT_STREQ("record-until-full", config.ToTraceOptionsString().c_str()); - - config = TraceConfig("", RECORD_CONTINUOUSLY); - EXPECT_EQ(RECORD_CONTINUOUSLY, config.GetTraceRecordMode()); - EXPECT_FALSE(config.IsSystraceEnabled()); - EXPECT_FALSE(config.IsArgumentFilterEnabled()); - EXPECT_STREQ("record-continuously", config.ToTraceOptionsString().c_str()); - - config = TraceConfig("", ECHO_TO_CONSOLE); - EXPECT_EQ(ECHO_TO_CONSOLE, config.GetTraceRecordMode()); - EXPECT_FALSE(config.IsSystraceEnabled()); - EXPECT_FALSE(config.IsArgumentFilterEnabled()); - EXPECT_STREQ("trace-to-console", config.ToTraceOptionsString().c_str()); - - config = TraceConfig("", RECORD_AS_MUCH_AS_POSSIBLE); - EXPECT_EQ(RECORD_AS_MUCH_AS_POSSIBLE, config.GetTraceRecordMode()); - EXPECT_FALSE(config.IsSystraceEnabled()); - EXPECT_FALSE(config.IsArgumentFilterEnabled()); - EXPECT_STREQ("record-as-much-as-possible", - config.ToTraceOptionsString().c_str()); - - // From category filter strings - config = TraceConfig("included,-excluded,inc_pattern*,-exc_pattern*", ""); - EXPECT_STREQ("included,inc_pattern*,-excluded,-exc_pattern*", - config.ToCategoryFilterString().c_str()); - - config = TraceConfig("only_inc_cat", ""); - EXPECT_STREQ("only_inc_cat", config.ToCategoryFilterString().c_str()); - - config = TraceConfig("-only_exc_cat", ""); - EXPECT_STREQ("-only_exc_cat", config.ToCategoryFilterString().c_str()); - - config = TraceConfig("disabled-by-default-cc,-excluded", ""); - EXPECT_STREQ("disabled-by-default-cc,-excluded", - config.ToCategoryFilterString().c_str()); - - config = TraceConfig("disabled-by-default-cc,included", ""); - EXPECT_STREQ("included,disabled-by-default-cc", - config.ToCategoryFilterString().c_str()); - - config = TraceConfig("DELAY(test.Delay1;16),included", ""); - EXPECT_STREQ("included,DELAY(test.Delay1;16)", - config.ToCategoryFilterString().c_str()); - - // From both trace options and category filter strings - config = TraceConfig("", ""); - EXPECT_EQ(RECORD_UNTIL_FULL, config.GetTraceRecordMode()); - EXPECT_FALSE(config.IsSystraceEnabled()); - EXPECT_FALSE(config.IsArgumentFilterEnabled()); - EXPECT_STREQ("", config.ToCategoryFilterString().c_str()); - EXPECT_STREQ("record-until-full", config.ToTraceOptionsString().c_str()); - - config = TraceConfig("included,-excluded,inc_pattern*,-exc_pattern*", - "enable-systrace, trace-to-console"); - EXPECT_EQ(ECHO_TO_CONSOLE, config.GetTraceRecordMode()); - EXPECT_TRUE(config.IsSystraceEnabled()); - EXPECT_FALSE(config.IsArgumentFilterEnabled()); - EXPECT_STREQ("included,inc_pattern*,-excluded,-exc_pattern*", - config.ToCategoryFilterString().c_str()); - EXPECT_STREQ("trace-to-console,enable-systrace", - config.ToTraceOptionsString().c_str()); - - // From both trace options and category filter strings with spaces. - config = TraceConfig(" included , -excluded, inc_pattern*, ,-exc_pattern* ", - "enable-systrace, ,trace-to-console "); - EXPECT_EQ(ECHO_TO_CONSOLE, config.GetTraceRecordMode()); - EXPECT_TRUE(config.IsSystraceEnabled()); - EXPECT_FALSE(config.IsArgumentFilterEnabled()); - EXPECT_STREQ("included,inc_pattern*,-excluded,-exc_pattern*", - config.ToCategoryFilterString().c_str()); - EXPECT_STREQ("trace-to-console,enable-systrace", - config.ToTraceOptionsString().c_str()); - - // From category filter string and TraceRecordMode - config = TraceConfig("included,-excluded,inc_pattern*,-exc_pattern*", - RECORD_CONTINUOUSLY); - EXPECT_EQ(RECORD_CONTINUOUSLY, config.GetTraceRecordMode()); - EXPECT_FALSE(config.IsSystraceEnabled()); - EXPECT_FALSE(config.IsArgumentFilterEnabled()); - EXPECT_STREQ("included,inc_pattern*,-excluded,-exc_pattern*", - config.ToCategoryFilterString().c_str()); - EXPECT_STREQ("record-continuously", config.ToTraceOptionsString().c_str()); -} - -TEST(TraceConfigTest, TraceConfigFromInvalidLegacyStrings) { - TraceConfig config("", "foo-bar-baz"); - EXPECT_EQ(RECORD_UNTIL_FULL, config.GetTraceRecordMode()); - EXPECT_FALSE(config.IsSystraceEnabled()); - EXPECT_FALSE(config.IsArgumentFilterEnabled()); - EXPECT_STREQ("", config.ToCategoryFilterString().c_str()); - EXPECT_STREQ("record-until-full", config.ToTraceOptionsString().c_str()); - - config = TraceConfig("arbitrary-category", "foo-bar-baz, enable-systrace"); - EXPECT_EQ(RECORD_UNTIL_FULL, config.GetTraceRecordMode()); - EXPECT_TRUE(config.IsSystraceEnabled()); - EXPECT_FALSE(config.IsArgumentFilterEnabled()); - EXPECT_STREQ("arbitrary-category", config.ToCategoryFilterString().c_str()); - EXPECT_STREQ("record-until-full,enable-systrace", - config.ToTraceOptionsString().c_str()); - - const char* const configs[] = { - "", - "DELAY(", - "DELAY(;", - "DELAY(;)", - "DELAY(test.Delay)", - "DELAY(test.Delay;)" - }; - for (size_t i = 0; i < arraysize(configs); i++) { - TraceConfig tc(configs[i], ""); - EXPECT_EQ(0u, tc.GetSyntheticDelayValues().size()); - } -} - -TEST(TraceConfigTest, ConstructDefaultTraceConfig) { - TraceConfig tc; - EXPECT_STREQ("", tc.ToCategoryFilterString().c_str()); - EXPECT_STREQ(kDefaultTraceConfigString, tc.ToString().c_str()); - CheckDefaultTraceConfigBehavior(tc); - - // Constructors from category filter string and trace option string. - TraceConfig tc_asterisk("*", ""); - EXPECT_STREQ("*", tc_asterisk.ToCategoryFilterString().c_str()); - CheckDefaultTraceConfigBehavior(tc_asterisk); - - TraceConfig tc_empty_category_filter("", ""); - EXPECT_STREQ("", tc_empty_category_filter.ToCategoryFilterString().c_str()); - EXPECT_STREQ(kDefaultTraceConfigString, - tc_empty_category_filter.ToString().c_str()); - CheckDefaultTraceConfigBehavior(tc_empty_category_filter); - - // Constructor from JSON formated config string. - TraceConfig tc_empty_json_string(""); - EXPECT_STREQ("", tc_empty_json_string.ToCategoryFilterString().c_str()); - EXPECT_STREQ(kDefaultTraceConfigString, - tc_empty_json_string.ToString().c_str()); - CheckDefaultTraceConfigBehavior(tc_empty_json_string); - - // Constructor from dictionary value. - DictionaryValue dict; - TraceConfig tc_dict(dict); - EXPECT_STREQ("", tc_dict.ToCategoryFilterString().c_str()); - EXPECT_STREQ(kDefaultTraceConfigString, tc_dict.ToString().c_str()); - CheckDefaultTraceConfigBehavior(tc_dict); -} - -TEST(TraceConfigTest, EmptyAndAsteriskCategoryFilterString) { - TraceConfig tc_empty("", ""); - TraceConfig tc_asterisk("*", ""); - - EXPECT_STREQ("", tc_empty.ToCategoryFilterString().c_str()); - EXPECT_STREQ("*", tc_asterisk.ToCategoryFilterString().c_str()); - - // Both fall back to default config. - CheckDefaultTraceConfigBehavior(tc_empty); - CheckDefaultTraceConfigBehavior(tc_asterisk); - - // They differ only for internal checking. - EXPECT_FALSE(tc_empty.category_filter().IsCategoryEnabled("Category1")); - EXPECT_FALSE( - tc_empty.category_filter().IsCategoryEnabled("not-excluded-category")); - EXPECT_TRUE(tc_asterisk.category_filter().IsCategoryEnabled("Category1")); - EXPECT_TRUE( - tc_asterisk.category_filter().IsCategoryEnabled("not-excluded-category")); -} - -TEST(TraceConfigTest, DisabledByDefaultCategoryFilterString) { - TraceConfig tc("foo,disabled-by-default-foo", ""); - EXPECT_STREQ("foo,disabled-by-default-foo", - tc.ToCategoryFilterString().c_str()); - EXPECT_TRUE(tc.IsCategoryGroupEnabled("foo")); - EXPECT_TRUE(tc.IsCategoryGroupEnabled("disabled-by-default-foo")); - EXPECT_FALSE(tc.IsCategoryGroupEnabled("bar")); - EXPECT_FALSE(tc.IsCategoryGroupEnabled("disabled-by-default-bar")); - - EXPECT_TRUE(tc.event_filters().empty()); - // Enabling only the disabled-by-default-* category means the default ones - // are also enabled. - tc = TraceConfig("disabled-by-default-foo", ""); - EXPECT_STREQ("disabled-by-default-foo", tc.ToCategoryFilterString().c_str()); - EXPECT_TRUE(tc.IsCategoryGroupEnabled("disabled-by-default-foo")); - EXPECT_TRUE(tc.IsCategoryGroupEnabled("foo")); - EXPECT_TRUE(tc.IsCategoryGroupEnabled("bar")); - EXPECT_FALSE(tc.IsCategoryGroupEnabled("disabled-by-default-bar")); -} - -TEST(TraceConfigTest, TraceConfigFromDict) { - // Passing in empty dictionary will result in default trace config. - DictionaryValue dict; - TraceConfig tc(dict); - EXPECT_STREQ(kDefaultTraceConfigString, tc.ToString().c_str()); - EXPECT_EQ(RECORD_UNTIL_FULL, tc.GetTraceRecordMode()); - EXPECT_FALSE(tc.IsSystraceEnabled()); - EXPECT_FALSE(tc.IsArgumentFilterEnabled()); - EXPECT_STREQ("", tc.ToCategoryFilterString().c_str()); - - std::unique_ptr<Value> default_value( - JSONReader::Read(kDefaultTraceConfigString)); - DCHECK(default_value); - const DictionaryValue* default_dict = nullptr; - bool is_dict = default_value->GetAsDictionary(&default_dict); - DCHECK(is_dict); - TraceConfig default_tc(*default_dict); - EXPECT_STREQ(kDefaultTraceConfigString, default_tc.ToString().c_str()); - EXPECT_EQ(RECORD_UNTIL_FULL, default_tc.GetTraceRecordMode()); - EXPECT_FALSE(default_tc.IsSystraceEnabled()); - EXPECT_FALSE(default_tc.IsArgumentFilterEnabled()); - EXPECT_STREQ("", default_tc.ToCategoryFilterString().c_str()); - - std::unique_ptr<Value> custom_value( - JSONReader::Read(kCustomTraceConfigString)); - DCHECK(custom_value); - const DictionaryValue* custom_dict = nullptr; - is_dict = custom_value->GetAsDictionary(&custom_dict); - DCHECK(is_dict); - TraceConfig custom_tc(*custom_dict); - EXPECT_STREQ(kCustomTraceConfigString, custom_tc.ToString().c_str()); - EXPECT_EQ(RECORD_CONTINUOUSLY, custom_tc.GetTraceRecordMode()); - EXPECT_TRUE(custom_tc.IsSystraceEnabled()); - EXPECT_TRUE(custom_tc.IsArgumentFilterEnabled()); - EXPECT_STREQ("included,inc_pattern*," - "disabled-by-default-cc,disabled-by-default-memory-infra," - "-excluded,-exc_pattern*," - "DELAY(test.Delay1;16),DELAY(test.Delay2;32)", - custom_tc.ToCategoryFilterString().c_str()); -} - -TEST(TraceConfigTest, TraceConfigFromValidString) { - // Using some non-empty config string. - const char config_string[] = - "{" - "\"enable_argument_filter\":true," - "\"enable_systrace\":true," - "\"event_filters\":[" - "{" - "\"excluded_categories\":[\"unfiltered_cat\"]," - "\"filter_args\":{\"event_name_whitelist\":[\"a snake\",\"a dog\"]}," - "\"filter_predicate\":\"event_whitelist_predicate\"," - "\"included_categories\":[\"*\"]" - "}" - "]," - "\"excluded_categories\":[\"excluded\",\"exc_pattern*\"]," - "\"included_categories\":[\"included\"," - "\"inc_pattern*\"," - "\"disabled-by-default-cc\"]," - "\"record_mode\":\"record-continuously\"," - "\"synthetic_delays\":[\"test.Delay1;16\",\"test.Delay2;32\"]" - "}"; - TraceConfig tc(config_string); - - EXPECT_STREQ(config_string, tc.ToString().c_str()); - EXPECT_EQ(RECORD_CONTINUOUSLY, tc.GetTraceRecordMode()); - EXPECT_TRUE(tc.IsSystraceEnabled()); - EXPECT_TRUE(tc.IsArgumentFilterEnabled()); - EXPECT_STREQ("included,inc_pattern*,disabled-by-default-cc,-excluded," - "-exc_pattern*,DELAY(test.Delay1;16),DELAY(test.Delay2;32)", - tc.ToCategoryFilterString().c_str()); - - EXPECT_TRUE(tc.category_filter().IsCategoryEnabled("included")); - EXPECT_TRUE(tc.category_filter().IsCategoryEnabled("inc_pattern_category")); - EXPECT_TRUE(tc.category_filter().IsCategoryEnabled("disabled-by-default-cc")); - EXPECT_FALSE(tc.category_filter().IsCategoryEnabled("excluded")); - EXPECT_FALSE(tc.category_filter().IsCategoryEnabled("exc_pattern_category")); - EXPECT_FALSE( - tc.category_filter().IsCategoryEnabled("disabled-by-default-others")); - EXPECT_FALSE( - tc.category_filter().IsCategoryEnabled("not-excluded-nor-included")); - - EXPECT_TRUE(tc.IsCategoryGroupEnabled("included")); - EXPECT_TRUE(tc.IsCategoryGroupEnabled("inc_pattern_category")); - EXPECT_TRUE(tc.IsCategoryGroupEnabled("disabled-by-default-cc")); - EXPECT_FALSE(tc.IsCategoryGroupEnabled("excluded")); - EXPECT_FALSE(tc.IsCategoryGroupEnabled("exc_pattern_category")); - EXPECT_FALSE(tc.IsCategoryGroupEnabled("disabled-by-default-others")); - EXPECT_FALSE(tc.IsCategoryGroupEnabled("not-excluded-nor-included")); - - EXPECT_TRUE(tc.IsCategoryGroupEnabled("included,excluded")); - EXPECT_FALSE(tc.IsCategoryGroupEnabled("excluded,exc_pattern_category")); - EXPECT_TRUE(tc.IsCategoryGroupEnabled("included,DELAY(test.Delay1;16)")); - EXPECT_FALSE(tc.IsCategoryGroupEnabled("DELAY(test.Delay1;16)")); - - EXPECT_EQ(2u, tc.GetSyntheticDelayValues().size()); - EXPECT_STREQ("test.Delay1;16", tc.GetSyntheticDelayValues()[0].c_str()); - EXPECT_STREQ("test.Delay2;32", tc.GetSyntheticDelayValues()[1].c_str()); - - EXPECT_EQ(tc.event_filters().size(), 1u); - const TraceConfig::EventFilterConfig& event_filter = tc.event_filters()[0]; - EXPECT_STREQ("event_whitelist_predicate", - event_filter.predicate_name().c_str()); - EXPECT_EQ(1u, event_filter.category_filter().included_categories().size()); - EXPECT_STREQ("*", - event_filter.category_filter().included_categories()[0].c_str()); - EXPECT_EQ(1u, event_filter.category_filter().excluded_categories().size()); - EXPECT_STREQ("unfiltered_cat", - event_filter.category_filter().excluded_categories()[0].c_str()); - EXPECT_TRUE(event_filter.filter_args()); - - std::string json_out; - base::JSONWriter::Write(*event_filter.filter_args(), &json_out); - EXPECT_STREQ(json_out.c_str(), - "{\"event_name_whitelist\":[\"a snake\",\"a dog\"]}"); - std::unordered_set<std::string> filter_values; - EXPECT_TRUE(event_filter.GetArgAsSet("event_name_whitelist", &filter_values)); - EXPECT_EQ(2u, filter_values.size()); - EXPECT_EQ(1u, filter_values.count("a snake")); - EXPECT_EQ(1u, filter_values.count("a dog")); - - const char config_string_2[] = "{\"included_categories\":[\"*\"]}"; - TraceConfig tc2(config_string_2); - EXPECT_TRUE(tc2.category_filter().IsCategoryEnabled( - "non-disabled-by-default-pattern")); - EXPECT_FALSE( - tc2.category_filter().IsCategoryEnabled("disabled-by-default-pattern")); - EXPECT_TRUE(tc2.IsCategoryGroupEnabled("non-disabled-by-default-pattern")); - EXPECT_FALSE(tc2.IsCategoryGroupEnabled("disabled-by-default-pattern")); - - // Clear - tc.Clear(); - EXPECT_STREQ(tc.ToString().c_str(), - "{" - "\"enable_argument_filter\":false," - "\"enable_systrace\":false," - "\"record_mode\":\"record-until-full\"" - "}"); -} - -TEST(TraceConfigTest, TraceConfigFromInvalidString) { - // The config string needs to be a dictionary correctly formatted as a JSON - // string. Otherwise, it will fall back to the default initialization. - TraceConfig tc(""); - EXPECT_STREQ(kDefaultTraceConfigString, tc.ToString().c_str()); - EXPECT_EQ(RECORD_UNTIL_FULL, tc.GetTraceRecordMode()); - EXPECT_FALSE(tc.IsSystraceEnabled()); - EXPECT_FALSE(tc.IsArgumentFilterEnabled()); - EXPECT_STREQ("", tc.ToCategoryFilterString().c_str()); - CheckDefaultTraceConfigBehavior(tc); - - tc = TraceConfig("This is an invalid config string."); - EXPECT_STREQ(kDefaultTraceConfigString, tc.ToString().c_str()); - EXPECT_EQ(RECORD_UNTIL_FULL, tc.GetTraceRecordMode()); - EXPECT_FALSE(tc.IsSystraceEnabled()); - EXPECT_FALSE(tc.IsArgumentFilterEnabled()); - EXPECT_STREQ("", tc.ToCategoryFilterString().c_str()); - CheckDefaultTraceConfigBehavior(tc); - - tc = TraceConfig("[\"This\", \"is\", \"not\", \"a\", \"dictionary\"]"); - EXPECT_STREQ(kDefaultTraceConfigString, tc.ToString().c_str()); - EXPECT_EQ(RECORD_UNTIL_FULL, tc.GetTraceRecordMode()); - EXPECT_FALSE(tc.IsSystraceEnabled()); - EXPECT_FALSE(tc.IsArgumentFilterEnabled()); - EXPECT_STREQ("", tc.ToCategoryFilterString().c_str()); - CheckDefaultTraceConfigBehavior(tc); - - tc = TraceConfig("{\"record_mode\": invalid-value-needs-double-quote}"); - EXPECT_STREQ(kDefaultTraceConfigString, tc.ToString().c_str()); - EXPECT_EQ(RECORD_UNTIL_FULL, tc.GetTraceRecordMode()); - EXPECT_FALSE(tc.IsSystraceEnabled()); - EXPECT_FALSE(tc.IsArgumentFilterEnabled()); - EXPECT_STREQ("", tc.ToCategoryFilterString().c_str()); - CheckDefaultTraceConfigBehavior(tc); - - // If the config string a dictionary formatted as a JSON string, it will - // initialize TraceConfig with best effort. - tc = TraceConfig("{}"); - EXPECT_EQ(RECORD_UNTIL_FULL, tc.GetTraceRecordMode()); - EXPECT_FALSE(tc.IsSystraceEnabled()); - EXPECT_FALSE(tc.IsArgumentFilterEnabled()); - EXPECT_STREQ("", tc.ToCategoryFilterString().c_str()); - CheckDefaultTraceConfigBehavior(tc); - - tc = TraceConfig("{\"arbitrary-key\":\"arbitrary-value\"}"); - EXPECT_EQ(RECORD_UNTIL_FULL, tc.GetTraceRecordMode()); - EXPECT_FALSE(tc.IsSystraceEnabled()); - EXPECT_FALSE(tc.IsArgumentFilterEnabled()); - EXPECT_STREQ("", tc.ToCategoryFilterString().c_str()); - CheckDefaultTraceConfigBehavior(tc); - - const char invalid_config_string[] = - "{" - "\"enable_systrace\":1," - "\"excluded_categories\":[\"excluded\"]," - "\"included_categories\":\"not a list\"," - "\"record_mode\":\"arbitrary-mode\"," - "\"synthetic_delays\":[\"test.Delay1;16\"," - "\"invalid-delay\"," - "\"test.Delay2;32\"]" - "}"; - tc = TraceConfig(invalid_config_string); - EXPECT_EQ(RECORD_UNTIL_FULL, tc.GetTraceRecordMode()); - EXPECT_FALSE(tc.IsSystraceEnabled()); - EXPECT_FALSE(tc.IsArgumentFilterEnabled()); - EXPECT_STREQ("-excluded,DELAY(test.Delay1;16),DELAY(test.Delay2;32)", - tc.ToCategoryFilterString().c_str()); - - const char invalid_config_string_2[] = - "{" - "\"included_categories\":[\"category\",\"disabled-by-default-pattern\"]," - "\"excluded_categories\":[\"category\",\"disabled-by-default-pattern\"]" - "}"; - tc = TraceConfig(invalid_config_string_2); - EXPECT_TRUE(tc.category_filter().IsCategoryEnabled("category")); - EXPECT_TRUE( - tc.category_filter().IsCategoryEnabled("disabled-by-default-pattern")); - EXPECT_TRUE(tc.IsCategoryGroupEnabled("category")); - EXPECT_TRUE(tc.IsCategoryGroupEnabled("disabled-by-default-pattern")); -} - -TEST(TraceConfigTest, MergingTraceConfigs) { - // Merge - TraceConfig tc; - TraceConfig tc2("included,-excluded,inc_pattern*,-exc_pattern*", ""); - tc.Merge(tc2); - EXPECT_STREQ("{" - "\"enable_argument_filter\":false," - "\"enable_systrace\":false," - "\"excluded_categories\":[\"excluded\",\"exc_pattern*\"]," - "\"record_mode\":\"record-until-full\"" - "}", - tc.ToString().c_str()); - - tc = TraceConfig("DELAY(test.Delay1;16)", ""); - tc2 = TraceConfig("DELAY(test.Delay2;32)", ""); - tc.Merge(tc2); - EXPECT_EQ(2u, tc.GetSyntheticDelayValues().size()); - EXPECT_STREQ("test.Delay1;16", tc.GetSyntheticDelayValues()[0].c_str()); - EXPECT_STREQ("test.Delay2;32", tc.GetSyntheticDelayValues()[1].c_str()); -} - -TEST(TraceConfigTest, IsCategoryGroupEnabled) { - // Enabling a disabled- category does not require all categories to be traced - // to be included. - TraceConfig tc("disabled-by-default-cc,-excluded", ""); - EXPECT_STREQ("disabled-by-default-cc,-excluded", - tc.ToCategoryFilterString().c_str()); - EXPECT_TRUE(tc.IsCategoryGroupEnabled("disabled-by-default-cc")); - EXPECT_TRUE(tc.IsCategoryGroupEnabled("some_other_group")); - EXPECT_FALSE(tc.IsCategoryGroupEnabled("excluded")); - - // Enabled a disabled- category and also including makes all categories to - // be traced require including. - tc = TraceConfig("disabled-by-default-cc,included", ""); - EXPECT_STREQ("included,disabled-by-default-cc", - tc.ToCategoryFilterString().c_str()); - EXPECT_TRUE(tc.IsCategoryGroupEnabled("disabled-by-default-cc")); - EXPECT_TRUE(tc.IsCategoryGroupEnabled("included")); - EXPECT_FALSE(tc.IsCategoryGroupEnabled("other_included")); - - // Excluding categories won't enable disabled-by-default ones with the - // excluded category is also present in the group. - tc = TraceConfig("-excluded", ""); - EXPECT_STREQ("-excluded", tc.ToCategoryFilterString().c_str()); - EXPECT_FALSE(tc.IsCategoryGroupEnabled("excluded,disabled-by-default-cc")); -} - -TEST(TraceConfigTest, IsCategoryNameAllowed) { - // Test that IsCategoryNameAllowed actually catches categories that are - // explicitly forbidden. This method is called in a DCHECK to assert that we - // don't have these types of strings as categories. - EXPECT_FALSE( - TraceConfigCategoryFilter::IsCategoryNameAllowed(" bad_category ")); - EXPECT_FALSE( - TraceConfigCategoryFilter::IsCategoryNameAllowed(" bad_category")); - EXPECT_FALSE( - TraceConfigCategoryFilter::IsCategoryNameAllowed("bad_category ")); - EXPECT_FALSE( - TraceConfigCategoryFilter::IsCategoryNameAllowed(" bad_category")); - EXPECT_FALSE( - TraceConfigCategoryFilter::IsCategoryNameAllowed("bad_category ")); - EXPECT_FALSE( - TraceConfigCategoryFilter::IsCategoryNameAllowed(" bad_category ")); - EXPECT_FALSE(TraceConfigCategoryFilter::IsCategoryNameAllowed("")); - EXPECT_TRUE( - TraceConfigCategoryFilter::IsCategoryNameAllowed("good_category")); -} - -TEST(TraceConfigTest, SetTraceOptionValues) { - TraceConfig tc; - EXPECT_EQ(RECORD_UNTIL_FULL, tc.GetTraceRecordMode()); - EXPECT_FALSE(tc.IsSystraceEnabled()); - - tc.SetTraceRecordMode(RECORD_AS_MUCH_AS_POSSIBLE); - EXPECT_EQ(RECORD_AS_MUCH_AS_POSSIBLE, tc.GetTraceRecordMode()); - - tc.EnableSystrace(); - EXPECT_TRUE(tc.IsSystraceEnabled()); -} - -TEST(TraceConfigTest, TraceConfigFromMemoryConfigString) { - std::string tc_str1 = - TraceConfigMemoryTestUtil::GetTraceConfig_PeriodicTriggers(200, 2000); - TraceConfig tc1(tc_str1); - EXPECT_EQ(tc_str1, tc1.ToString()); - TraceConfig tc2( - TraceConfigMemoryTestUtil::GetTraceConfig_LegacyPeriodicTriggers(200, - 2000)); - EXPECT_EQ(tc_str1, tc2.ToString()); - - EXPECT_TRUE(tc1.IsCategoryGroupEnabled(MemoryDumpManager::kTraceCategory)); - ASSERT_EQ(2u, tc1.memory_dump_config().triggers.size()); - - EXPECT_EQ(200u, - tc1.memory_dump_config().triggers[0].min_time_between_dumps_ms); - EXPECT_EQ(MemoryDumpLevelOfDetail::LIGHT, - tc1.memory_dump_config().triggers[0].level_of_detail); - - EXPECT_EQ(2000u, - tc1.memory_dump_config().triggers[1].min_time_between_dumps_ms); - EXPECT_EQ(MemoryDumpLevelOfDetail::DETAILED, - tc1.memory_dump_config().triggers[1].level_of_detail); - EXPECT_EQ( - 2048u, - tc1.memory_dump_config().heap_profiler_options.breakdown_threshold_bytes); - - std::string tc_str3 = - TraceConfigMemoryTestUtil::GetTraceConfig_BackgroundTrigger( - 1 /* period_ms */); - TraceConfig tc3(tc_str3); - EXPECT_EQ(tc_str3, tc3.ToString()); - EXPECT_TRUE(tc3.IsCategoryGroupEnabled(MemoryDumpManager::kTraceCategory)); - ASSERT_EQ(1u, tc3.memory_dump_config().triggers.size()); - EXPECT_EQ(1u, tc3.memory_dump_config().triggers[0].min_time_between_dumps_ms); - EXPECT_EQ(MemoryDumpLevelOfDetail::BACKGROUND, - tc3.memory_dump_config().triggers[0].level_of_detail); - - std::string tc_str4 = - TraceConfigMemoryTestUtil::GetTraceConfig_PeakDetectionTrigger( - 1 /*heavy_period */); - TraceConfig tc4(tc_str4); - EXPECT_EQ(tc_str4, tc4.ToString()); - ASSERT_EQ(1u, tc4.memory_dump_config().triggers.size()); - EXPECT_EQ(1u, tc4.memory_dump_config().triggers[0].min_time_between_dumps_ms); - EXPECT_EQ(MemoryDumpLevelOfDetail::DETAILED, - tc4.memory_dump_config().triggers[0].level_of_detail); -} - -TEST(TraceConfigTest, EmptyMemoryDumpConfigTest) { - // Empty trigger list should also be specified when converting back to string. - TraceConfig tc(TraceConfigMemoryTestUtil::GetTraceConfig_EmptyTriggers()); - EXPECT_EQ(TraceConfigMemoryTestUtil::GetTraceConfig_EmptyTriggers(), - tc.ToString()); - EXPECT_EQ(0u, tc.memory_dump_config().triggers.size()); - EXPECT_EQ( - TraceConfig::MemoryDumpConfig::HeapProfiler :: - kDefaultBreakdownThresholdBytes, - tc.memory_dump_config().heap_profiler_options.breakdown_threshold_bytes); -} - -TEST(TraceConfigTest, LegacyStringToMemoryDumpConfig) { - TraceConfig tc(MemoryDumpManager::kTraceCategory, ""); - EXPECT_TRUE(tc.IsCategoryGroupEnabled(MemoryDumpManager::kTraceCategory)); - EXPECT_NE(std::string::npos, tc.ToString().find("memory_dump_config")); - EXPECT_EQ(2u, tc.memory_dump_config().triggers.size()); - EXPECT_EQ( - TraceConfig::MemoryDumpConfig::HeapProfiler :: - kDefaultBreakdownThresholdBytes, - tc.memory_dump_config().heap_profiler_options.breakdown_threshold_bytes); -} - -} // namespace trace_event -} // namespace base diff --git a/base/trace_event/trace_event.h b/base/trace_event/trace_event.h index 51e6927cbd..9abb8887b0 100644 --- a/base/trace_event/trace_event.h +++ b/base/trace_event/trace_event.h @@ -5,6 +5,43 @@ #ifndef BASE_TRACE_EVENT_TRACE_EVENT_H_ #define BASE_TRACE_EVENT_TRACE_EVENT_H_ +// Replace with stub implementation. +#if 1 +#include "base/trace_event/common/trace_event_common.h" +#include "base/trace_event/heap_profiler.h" + +// To avoid -Wunused-* errors, eat expression by macro. +namespace libchrome_internal { +template <typename... Args> void Ignore(Args&&... args) {} +} +#define INTERNAL_IGNORE(...) \ + (false ? libchrome_internal::Ignore(__VA_ARGS__) : (void) 0) + +// Body is effectively empty. +#define INTERNAL_TRACE_EVENT_ADD_SCOPED(...) INTERNAL_IGNORE(__VA_ARGS__) +#define INTERNAL_TRACE_TASK_EXECUTION(...) +#define INTERNAL_TRACE_EVENT_ADD_SCOPED_WITH_FLOW(...) \ + INTERNAL_IGNORE(__VA_ARGS__) +#define TRACE_ID_MANGLE(val) (val) + +namespace base { +namespace trace_event { + +class TraceLog { + public: + static TraceLog* GetInstance() { + static TraceLog instance; + return &instance; + } + + pid_t process_id() { return 0; } + void SetCurrentThreadBlocksMessageLoop() {} +}; + +} // namespace trace_event +} // namespace base +#else + // This header file defines implementation details of how the trace macros in // trace_event_common.h collect and store trace events. Anything not // implementation-specific should go in trace_event_common.h instead of here. @@ -1115,4 +1152,5 @@ template<typename IDType> class TraceScopedTrackableObject { } // namespace trace_event } // namespace base +#endif #endif // BASE_TRACE_EVENT_TRACE_EVENT_H_ diff --git a/base/trace_event/trace_event_argument.cc b/base/trace_event/trace_event_argument.cc deleted file mode 100644 index db702b6231..0000000000 --- a/base/trace_event/trace_event_argument.cc +++ /dev/null @@ -1,473 +0,0 @@ -// Copyright (c) 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/trace_event/trace_event_argument.h" - -#include <stdint.h> - -#include <utility> - -#include "base/bits.h" -#include "base/json/json_writer.h" -#include "base/memory/ptr_util.h" -#include "base/trace_event/trace_event_memory_overhead.h" -#include "base/values.h" - -namespace base { -namespace trace_event { - -namespace { -const char kTypeStartDict = '{'; -const char kTypeEndDict = '}'; -const char kTypeStartArray = '['; -const char kTypeEndArray = ']'; -const char kTypeBool = 'b'; -const char kTypeInt = 'i'; -const char kTypeDouble = 'd'; -const char kTypeString = 's'; -const char kTypeCStr = '*'; - -#ifndef NDEBUG -const bool kStackTypeDict = false; -const bool kStackTypeArray = true; -#define DCHECK_CURRENT_CONTAINER_IS(x) DCHECK_EQ(x, nesting_stack_.back()) -#define DCHECK_CONTAINER_STACK_DEPTH_EQ(x) DCHECK_EQ(x, nesting_stack_.size()) -#define DEBUG_PUSH_CONTAINER(x) nesting_stack_.push_back(x) -#define DEBUG_POP_CONTAINER() nesting_stack_.pop_back() -#else -#define DCHECK_CURRENT_CONTAINER_IS(x) do {} while (0) -#define DCHECK_CONTAINER_STACK_DEPTH_EQ(x) do {} while (0) -#define DEBUG_PUSH_CONTAINER(x) do {} while (0) -#define DEBUG_POP_CONTAINER() do {} while (0) -#endif - -inline void WriteKeyNameAsRawPtr(Pickle& pickle, const char* ptr) { - pickle.WriteBytes(&kTypeCStr, 1); - pickle.WriteUInt64(static_cast<uint64_t>(reinterpret_cast<uintptr_t>(ptr))); -} - -inline void WriteKeyNameWithCopy(Pickle& pickle, base::StringPiece str) { - pickle.WriteBytes(&kTypeString, 1); - pickle.WriteString(str); -} - -std::string ReadKeyName(PickleIterator& pickle_iterator) { - const char* type = nullptr; - bool res = pickle_iterator.ReadBytes(&type, 1); - std::string key_name; - if (res && *type == kTypeCStr) { - uint64_t ptr_value = 0; - res = pickle_iterator.ReadUInt64(&ptr_value); - key_name = reinterpret_cast<const char*>(static_cast<uintptr_t>(ptr_value)); - } else if (res && *type == kTypeString) { - res = pickle_iterator.ReadString(&key_name); - } - DCHECK(res); - return key_name; -} -} // namespace - -TracedValue::TracedValue() : TracedValue(0) { -} - -TracedValue::TracedValue(size_t capacity) { - DEBUG_PUSH_CONTAINER(kStackTypeDict); - if (capacity) - pickle_.Reserve(capacity); -} - -TracedValue::~TracedValue() { - DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict); - DEBUG_POP_CONTAINER(); - DCHECK_CONTAINER_STACK_DEPTH_EQ(0u); -} - -void TracedValue::SetInteger(const char* name, int value) { - DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict); - pickle_.WriteBytes(&kTypeInt, 1); - pickle_.WriteInt(value); - WriteKeyNameAsRawPtr(pickle_, name); -} - -void TracedValue::SetIntegerWithCopiedName(base::StringPiece name, int value) { - DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict); - pickle_.WriteBytes(&kTypeInt, 1); - pickle_.WriteInt(value); - WriteKeyNameWithCopy(pickle_, name); -} - -void TracedValue::SetDouble(const char* name, double value) { - DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict); - pickle_.WriteBytes(&kTypeDouble, 1); - pickle_.WriteDouble(value); - WriteKeyNameAsRawPtr(pickle_, name); -} - -void TracedValue::SetDoubleWithCopiedName(base::StringPiece name, - double value) { - DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict); - pickle_.WriteBytes(&kTypeDouble, 1); - pickle_.WriteDouble(value); - WriteKeyNameWithCopy(pickle_, name); -} - -void TracedValue::SetBoolean(const char* name, bool value) { - DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict); - pickle_.WriteBytes(&kTypeBool, 1); - pickle_.WriteBool(value); - WriteKeyNameAsRawPtr(pickle_, name); -} - -void TracedValue::SetBooleanWithCopiedName(base::StringPiece name, - bool value) { - DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict); - pickle_.WriteBytes(&kTypeBool, 1); - pickle_.WriteBool(value); - WriteKeyNameWithCopy(pickle_, name); -} - -void TracedValue::SetString(const char* name, base::StringPiece value) { - DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict); - pickle_.WriteBytes(&kTypeString, 1); - pickle_.WriteString(value); - WriteKeyNameAsRawPtr(pickle_, name); -} - -void TracedValue::SetStringWithCopiedName(base::StringPiece name, - base::StringPiece value) { - DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict); - pickle_.WriteBytes(&kTypeString, 1); - pickle_.WriteString(value); - WriteKeyNameWithCopy(pickle_, name); -} - -void TracedValue::SetValue(const char* name, const TracedValue& value) { - DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict); - BeginDictionary(name); - pickle_.WriteBytes(value.pickle_.payload(), - static_cast<int>(value.pickle_.payload_size())); - EndDictionary(); -} - -void TracedValue::SetValueWithCopiedName(base::StringPiece name, - const TracedValue& value) { - DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict); - BeginDictionaryWithCopiedName(name); - pickle_.WriteBytes(value.pickle_.payload(), - static_cast<int>(value.pickle_.payload_size())); - EndDictionary(); -} - -void TracedValue::BeginDictionary(const char* name) { - DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict); - DEBUG_PUSH_CONTAINER(kStackTypeDict); - pickle_.WriteBytes(&kTypeStartDict, 1); - WriteKeyNameAsRawPtr(pickle_, name); -} - -void TracedValue::BeginDictionaryWithCopiedName(base::StringPiece name) { - DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict); - DEBUG_PUSH_CONTAINER(kStackTypeDict); - pickle_.WriteBytes(&kTypeStartDict, 1); - WriteKeyNameWithCopy(pickle_, name); -} - -void TracedValue::BeginArray(const char* name) { - DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict); - DEBUG_PUSH_CONTAINER(kStackTypeArray); - pickle_.WriteBytes(&kTypeStartArray, 1); - WriteKeyNameAsRawPtr(pickle_, name); -} - -void TracedValue::BeginArrayWithCopiedName(base::StringPiece name) { - DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict); - DEBUG_PUSH_CONTAINER(kStackTypeArray); - pickle_.WriteBytes(&kTypeStartArray, 1); - WriteKeyNameWithCopy(pickle_, name); -} - -void TracedValue::EndDictionary() { - DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict); - DEBUG_POP_CONTAINER(); - pickle_.WriteBytes(&kTypeEndDict, 1); -} - -void TracedValue::AppendInteger(int value) { - DCHECK_CURRENT_CONTAINER_IS(kStackTypeArray); - pickle_.WriteBytes(&kTypeInt, 1); - pickle_.WriteInt(value); -} - -void TracedValue::AppendDouble(double value) { - DCHECK_CURRENT_CONTAINER_IS(kStackTypeArray); - pickle_.WriteBytes(&kTypeDouble, 1); - pickle_.WriteDouble(value); -} - -void TracedValue::AppendBoolean(bool value) { - DCHECK_CURRENT_CONTAINER_IS(kStackTypeArray); - pickle_.WriteBytes(&kTypeBool, 1); - pickle_.WriteBool(value); -} - -void TracedValue::AppendString(base::StringPiece value) { - DCHECK_CURRENT_CONTAINER_IS(kStackTypeArray); - pickle_.WriteBytes(&kTypeString, 1); - pickle_.WriteString(value); -} - -void TracedValue::BeginArray() { - DCHECK_CURRENT_CONTAINER_IS(kStackTypeArray); - DEBUG_PUSH_CONTAINER(kStackTypeArray); - pickle_.WriteBytes(&kTypeStartArray, 1); -} - -void TracedValue::BeginDictionary() { - DCHECK_CURRENT_CONTAINER_IS(kStackTypeArray); - DEBUG_PUSH_CONTAINER(kStackTypeDict); - pickle_.WriteBytes(&kTypeStartDict, 1); -} - -void TracedValue::EndArray() { - DCHECK_CURRENT_CONTAINER_IS(kStackTypeArray); - DEBUG_POP_CONTAINER(); - pickle_.WriteBytes(&kTypeEndArray, 1); -} - -void TracedValue::SetValue(const char* name, - std::unique_ptr<base::Value> value) { - SetBaseValueWithCopiedName(name, *value); -} - -void TracedValue::SetBaseValueWithCopiedName(base::StringPiece name, - const base::Value& value) { - DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict); - switch (value.GetType()) { - case base::Value::Type::NONE: - case base::Value::Type::BINARY: - NOTREACHED(); - break; - - case base::Value::Type::BOOLEAN: { - bool bool_value; - value.GetAsBoolean(&bool_value); - SetBooleanWithCopiedName(name, bool_value); - } break; - - case base::Value::Type::INTEGER: { - int int_value; - value.GetAsInteger(&int_value); - SetIntegerWithCopiedName(name, int_value); - } break; - - case base::Value::Type::DOUBLE: { - double double_value; - value.GetAsDouble(&double_value); - SetDoubleWithCopiedName(name, double_value); - } break; - - case base::Value::Type::STRING: { - const Value* string_value; - value.GetAsString(&string_value); - SetStringWithCopiedName(name, string_value->GetString()); - } break; - - case base::Value::Type::DICTIONARY: { - const DictionaryValue* dict_value; - value.GetAsDictionary(&dict_value); - BeginDictionaryWithCopiedName(name); - for (DictionaryValue::Iterator it(*dict_value); !it.IsAtEnd(); - it.Advance()) { - SetBaseValueWithCopiedName(it.key(), it.value()); - } - EndDictionary(); - } break; - - case base::Value::Type::LIST: { - const ListValue* list_value; - value.GetAsList(&list_value); - BeginArrayWithCopiedName(name); - for (const auto& base_value : *list_value) - AppendBaseValue(*base_value); - EndArray(); - } break; - } -} - -void TracedValue::AppendBaseValue(const base::Value& value) { - DCHECK_CURRENT_CONTAINER_IS(kStackTypeArray); - switch (value.GetType()) { - case base::Value::Type::NONE: - case base::Value::Type::BINARY: - NOTREACHED(); - break; - - case base::Value::Type::BOOLEAN: { - bool bool_value; - value.GetAsBoolean(&bool_value); - AppendBoolean(bool_value); - } break; - - case base::Value::Type::INTEGER: { - int int_value; - value.GetAsInteger(&int_value); - AppendInteger(int_value); - } break; - - case base::Value::Type::DOUBLE: { - double double_value; - value.GetAsDouble(&double_value); - AppendDouble(double_value); - } break; - - case base::Value::Type::STRING: { - const Value* string_value; - value.GetAsString(&string_value); - AppendString(string_value->GetString()); - } break; - - case base::Value::Type::DICTIONARY: { - const DictionaryValue* dict_value; - value.GetAsDictionary(&dict_value); - BeginDictionary(); - for (DictionaryValue::Iterator it(*dict_value); !it.IsAtEnd(); - it.Advance()) { - SetBaseValueWithCopiedName(it.key(), it.value()); - } - EndDictionary(); - } break; - - case base::Value::Type::LIST: { - const ListValue* list_value; - value.GetAsList(&list_value); - BeginArray(); - for (const auto& base_value : *list_value) - AppendBaseValue(*base_value); - EndArray(); - } break; - } -} - -std::unique_ptr<base::Value> TracedValue::ToBaseValue() const { - std::unique_ptr<DictionaryValue> root(new DictionaryValue); - DictionaryValue* cur_dict = root.get(); - ListValue* cur_list = nullptr; - std::vector<Value*> stack; - PickleIterator it(pickle_); - const char* type; - - while (it.ReadBytes(&type, 1)) { - DCHECK((cur_dict && !cur_list) || (cur_list && !cur_dict)); - switch (*type) { - case kTypeStartDict: { - auto* new_dict = new DictionaryValue(); - if (cur_dict) { - cur_dict->SetWithoutPathExpansion(ReadKeyName(it), - WrapUnique(new_dict)); - stack.push_back(cur_dict); - cur_dict = new_dict; - } else { - cur_list->Append(WrapUnique(new_dict)); - stack.push_back(cur_list); - cur_list = nullptr; - cur_dict = new_dict; - } - } break; - - case kTypeEndArray: - case kTypeEndDict: { - if (stack.back()->GetAsDictionary(&cur_dict)) { - cur_list = nullptr; - } else if (stack.back()->GetAsList(&cur_list)) { - cur_dict = nullptr; - } - stack.pop_back(); - } break; - - case kTypeStartArray: { - auto* new_list = new ListValue(); - if (cur_dict) { - cur_dict->SetWithoutPathExpansion(ReadKeyName(it), - WrapUnique(new_list)); - stack.push_back(cur_dict); - cur_dict = nullptr; - cur_list = new_list; - } else { - cur_list->Append(WrapUnique(new_list)); - stack.push_back(cur_list); - cur_list = new_list; - } - } break; - - case kTypeBool: { - bool value; - CHECK(it.ReadBool(&value)); - if (cur_dict) { - cur_dict->SetBooleanWithoutPathExpansion(ReadKeyName(it), value); - } else { - cur_list->AppendBoolean(value); - } - } break; - - case kTypeInt: { - int value; - CHECK(it.ReadInt(&value)); - if (cur_dict) { - cur_dict->SetIntegerWithoutPathExpansion(ReadKeyName(it), value); - } else { - cur_list->AppendInteger(value); - } - } break; - - case kTypeDouble: { - double value; - CHECK(it.ReadDouble(&value)); - if (cur_dict) { - cur_dict->SetDoubleWithoutPathExpansion(ReadKeyName(it), value); - } else { - cur_list->AppendDouble(value); - } - } break; - - case kTypeString: { - std::string value; - CHECK(it.ReadString(&value)); - if (cur_dict) { - cur_dict->SetStringWithoutPathExpansion(ReadKeyName(it), value); - } else { - cur_list->AppendString(value); - } - } break; - - default: - NOTREACHED(); - } - } - DCHECK(stack.empty()); - return std::move(root); -} - -void TracedValue::AppendAsTraceFormat(std::string* out) const { - DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict); - DCHECK_CONTAINER_STACK_DEPTH_EQ(1u); - - // TODO(primiano): this could be smarter, skip the ToBaseValue encoding and - // produce the JSON on its own. This will require refactoring JSONWriter - // to decouple the base::Value traversal from the JSON writing bits - std::string tmp; - JSONWriter::Write(*ToBaseValue(), &tmp); - *out += tmp; -} - -void TracedValue::EstimateTraceMemoryOverhead( - TraceEventMemoryOverhead* overhead) { - overhead->Add("TracedValue", - /* allocated size */ - pickle_.GetTotalAllocatedSize(), - /* resident size */ - pickle_.size()); -} - -} // namespace trace_event -} // namespace base diff --git a/base/trace_event/trace_event_argument.h b/base/trace_event/trace_event_argument.h deleted file mode 100644 index 81d8c0172a..0000000000 --- a/base/trace_event/trace_event_argument.h +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_TRACE_EVENT_TRACE_EVENT_ARGUMENT_H_ -#define BASE_TRACE_EVENT_TRACE_EVENT_ARGUMENT_H_ - -#include <stddef.h> - -#include <memory> -#include <string> -#include <vector> - -#include "base/macros.h" -#include "base/pickle.h" -#include "base/strings/string_piece.h" -#include "base/trace_event/trace_event_impl.h" - -namespace base { - -class Value; - -namespace trace_event { - -class BASE_EXPORT TracedValue : public ConvertableToTraceFormat { - public: - TracedValue(); - explicit TracedValue(size_t capacity); - ~TracedValue() override; - - void EndDictionary(); - void EndArray(); - - // These methods assume that |name| is a long lived "quoted" string. - void SetInteger(const char* name, int value); - void SetDouble(const char* name, double value); - void SetBoolean(const char* name, bool value); - void SetString(const char* name, base::StringPiece value); - void SetValue(const char* name, const TracedValue& value); - void BeginDictionary(const char* name); - void BeginArray(const char* name); - - // These, instead, can be safely passed a temporary string. - void SetIntegerWithCopiedName(base::StringPiece name, int value); - void SetDoubleWithCopiedName(base::StringPiece name, double value); - void SetBooleanWithCopiedName(base::StringPiece name, bool value); - void SetStringWithCopiedName(base::StringPiece name, - base::StringPiece value); - void SetValueWithCopiedName(base::StringPiece name, - const TracedValue& value); - void BeginDictionaryWithCopiedName(base::StringPiece name); - void BeginArrayWithCopiedName(base::StringPiece name); - - void AppendInteger(int); - void AppendDouble(double); - void AppendBoolean(bool); - void AppendString(base::StringPiece); - void BeginArray(); - void BeginDictionary(); - - // ConvertableToTraceFormat implementation. - void AppendAsTraceFormat(std::string* out) const override; - - void EstimateTraceMemoryOverhead(TraceEventMemoryOverhead* overhead) override; - - // DEPRECATED: do not use, here only for legacy reasons. These methods causes - // a copy-and-translation of the base::Value into the equivalent TracedValue. - // TODO(primiano): migrate the (three) existing clients to the cheaper - // SetValue(TracedValue) API. crbug.com/495628. - void SetValue(const char* name, std::unique_ptr<base::Value> value); - void SetBaseValueWithCopiedName(base::StringPiece name, - const base::Value& value); - void AppendBaseValue(const base::Value& value); - - // Public for tests only. - std::unique_ptr<base::Value> ToBaseValue() const; - - private: - Pickle pickle_; - -#ifndef NDEBUG - // In debug builds checks the pairings of {Start,End}{Dictionary,Array} - std::vector<bool> nesting_stack_; -#endif - - DISALLOW_COPY_AND_ASSIGN(TracedValue); -}; - -} // namespace trace_event -} // namespace base - -#endif // BASE_TRACE_EVENT_TRACE_EVENT_ARGUMENT_H_ diff --git a/base/trace_event/trace_event_argument_unittest.cc b/base/trace_event/trace_event_argument_unittest.cc deleted file mode 100644 index aef8441c8e..0000000000 --- a/base/trace_event/trace_event_argument_unittest.cc +++ /dev/null @@ -1,165 +0,0 @@ -// Copyright (c) 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/trace_event/trace_event_argument.h" - -#include <stddef.h> - -#include <utility> - -#include "base/memory/ptr_util.h" -#include "base/values.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { -namespace trace_event { - -TEST(TraceEventArgumentTest, FlatDictionary) { - std::unique_ptr<TracedValue> value(new TracedValue()); - value->SetInteger("int", 2014); - value->SetDouble("double", 0.0); - value->SetBoolean("bool", true); - value->SetString("string", "string"); - std::string json = "PREFIX"; - value->AppendAsTraceFormat(&json); - EXPECT_EQ( - "PREFIX{\"bool\":true,\"double\":0.0,\"int\":2014,\"string\":\"string\"}", - json); -} - -TEST(TraceEventArgumentTest, NoDotPathExpansion) { - std::unique_ptr<TracedValue> value(new TracedValue()); - value->SetInteger("in.t", 2014); - value->SetDouble("doub.le", 0.0); - value->SetBoolean("bo.ol", true); - value->SetString("str.ing", "str.ing"); - std::string json; - value->AppendAsTraceFormat(&json); - EXPECT_EQ( - "{\"bo.ol\":true,\"doub.le\":0.0,\"in.t\":2014,\"str.ing\":\"str.ing\"}", - json); -} - -TEST(TraceEventArgumentTest, Hierarchy) { - std::unique_ptr<TracedValue> value(new TracedValue()); - value->SetInteger("i0", 2014); - value->BeginDictionary("dict1"); - value->SetInteger("i1", 2014); - value->BeginDictionary("dict2"); - value->SetBoolean("b2", false); - value->EndDictionary(); - value->SetString("s1", "foo"); - value->EndDictionary(); - value->SetDouble("d0", 0.0); - value->SetBoolean("b0", true); - value->BeginArray("a1"); - value->AppendInteger(1); - value->AppendBoolean(true); - value->BeginDictionary(); - value->SetInteger("i2", 3); - value->EndDictionary(); - value->EndArray(); - value->SetString("s0", "foo"); - std::string json; - value->AppendAsTraceFormat(&json); - EXPECT_EQ( - "{\"a1\":[1,true,{\"i2\":3}],\"b0\":true,\"d0\":0.0,\"dict1\":{\"dict2\":" - "{\"b2\":false},\"i1\":2014,\"s1\":\"foo\"},\"i0\":2014,\"s0\":" - "\"foo\"}", - json); -} - -TEST(TraceEventArgumentTest, LongStrings) { - std::string kLongString = "supercalifragilisticexpialidocious"; - std::string kLongString2 = "0123456789012345678901234567890123456789"; - char kLongString3[4096]; - for (size_t i = 0; i < sizeof(kLongString3); ++i) - kLongString3[i] = 'a' + (i % 25); - kLongString3[sizeof(kLongString3) - 1] = '\0'; - - std::unique_ptr<TracedValue> value(new TracedValue()); - value->SetString("a", "short"); - value->SetString("b", kLongString); - value->BeginArray("c"); - value->AppendString(kLongString2); - value->AppendString(""); - value->BeginDictionary(); - value->SetString("a", kLongString3); - value->EndDictionary(); - value->EndArray(); - - std::string json; - value->AppendAsTraceFormat(&json); - EXPECT_EQ("{\"a\":\"short\",\"b\":\"" + kLongString + "\",\"c\":[\"" + - kLongString2 + "\",\"\",{\"a\":\"" + kLongString3 + "\"}]}", - json); -} - -TEST(TraceEventArgumentTest, PassBaseValue) { - Value int_value(42); - Value bool_value(true); - Value double_value(42.0f); - - auto dict_value = WrapUnique(new DictionaryValue); - dict_value->SetBoolean("bool", true); - dict_value->SetInteger("int", 42); - dict_value->SetDouble("double", 42.0f); - dict_value->SetString("string", std::string("a") + "b"); - dict_value->SetString("string", std::string("a") + "b"); - - auto list_value = WrapUnique(new ListValue); - list_value->AppendBoolean(false); - list_value->AppendInteger(1); - list_value->AppendString("in_list"); - list_value->Append(std::move(dict_value)); - - std::unique_ptr<TracedValue> value(new TracedValue()); - value->BeginDictionary("outer_dict"); - value->SetValue("inner_list", std::move(list_value)); - value->EndDictionary(); - - dict_value.reset(); - list_value.reset(); - - std::string json; - value->AppendAsTraceFormat(&json); - EXPECT_EQ( - "{\"outer_dict\":{\"inner_list\":[false,1,\"in_list\",{\"bool\":true," - "\"double\":42.0,\"int\":42,\"string\":\"ab\"}]}}", - json); -} - -TEST(TraceEventArgumentTest, PassTracedValue) { - auto dict_value = MakeUnique<TracedValue>(); - dict_value->SetInteger("a", 1); - - auto nested_dict_value = MakeUnique<TracedValue>(); - nested_dict_value->SetInteger("b", 2); - nested_dict_value->BeginArray("c"); - nested_dict_value->AppendString("foo"); - nested_dict_value->EndArray(); - - dict_value->SetValue("e", *nested_dict_value); - - // Check the merged result. - std::string json; - dict_value->AppendAsTraceFormat(&json); - EXPECT_EQ("{\"a\":1,\"e\":{\"b\":2,\"c\":[\"foo\"]}}", json); - - // Check that the passed nestd dict was left unouthced. - json = ""; - nested_dict_value->AppendAsTraceFormat(&json); - EXPECT_EQ("{\"b\":2,\"c\":[\"foo\"]}", json); - - // And that it is still usable. - nested_dict_value->SetInteger("f", 3); - nested_dict_value->BeginDictionary("g"); - nested_dict_value->EndDictionary(); - json = ""; - nested_dict_value->AppendAsTraceFormat(&json); - EXPECT_EQ("{\"b\":2,\"c\":[\"foo\"],\"f\":3,\"g\":{}}", json); -} - -} // namespace trace_event -} // namespace base diff --git a/base/trace_event/trace_event_filter.cc b/base/trace_event/trace_event_filter.cc deleted file mode 100644 index 6265295864..0000000000 --- a/base/trace_event/trace_event_filter.cc +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/trace_event/trace_event_filter.h" - -namespace base { -namespace trace_event { - -TraceEventFilter::TraceEventFilter() {} -TraceEventFilter::~TraceEventFilter() {} - -void TraceEventFilter::EndEvent(const char* category_name, - const char* event_name) const {} - -} // namespace trace_event -} // namespace base diff --git a/base/trace_event/trace_event_filter.h b/base/trace_event/trace_event_filter.h deleted file mode 100644 index 48c6711432..0000000000 --- a/base/trace_event/trace_event_filter.h +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_TRACE_EVENT_TRACE_EVENT_FILTER_H_ -#define BASE_TRACE_EVENT_TRACE_EVENT_FILTER_H_ - -#include <memory> - -#include "base/base_export.h" -#include "base/macros.h" - -namespace base { -namespace trace_event { - -class TraceEvent; - -// TraceEventFilter is like iptables for TRACE_EVENT macros. Filters can be -// enabled on a per-category basis, hence a single filter instance can serve -// more than a TraceCategory. There are two use cases for filters: -// 1. Snooping TRACE_EVENT macros without adding them to the TraceLog. This is -// possible by setting the ENABLED_FOR_FILTERING flag on a category w/o -// ENABLED_FOR_RECORDING (see TraceConfig for user-facing configuration). -// 2. Filtering TRACE_EVENT macros before they are added to the TraceLog. This -// requires both the ENABLED_FOR_FILTERING and ENABLED_FOR_RECORDING flags -// on the category. -// More importantly, filters must be thread-safe. The FilterTraceEvent and -// EndEvent methods can be called concurrently as trace macros are hit on -// different threads. -class BASE_EXPORT TraceEventFilter { - public: - TraceEventFilter(); - virtual ~TraceEventFilter(); - - // If the category is ENABLED_FOR_RECORDING, the event is added iff all the - // filters enabled for the category return true. false causes the event to be - // discarded. - virtual bool FilterTraceEvent(const TraceEvent& trace_event) const = 0; - - // Notifies the end of a duration event when the RAII macro goes out of scope. - virtual void EndEvent(const char* category_name, - const char* event_name) const; - - private: - DISALLOW_COPY_AND_ASSIGN(TraceEventFilter); -}; - -} // namespace trace_event -} // namespace base - -#endif // BASE_TRACE_EVENT_TRACE_EVENT_FILTER_H_ diff --git a/base/trace_event/trace_event_filter_test_utils.cc b/base/trace_event/trace_event_filter_test_utils.cc deleted file mode 100644 index 06548b049a..0000000000 --- a/base/trace_event/trace_event_filter_test_utils.cc +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/trace_event/trace_event_filter_test_utils.h" - -#include "base/logging.h" - -namespace base { -namespace trace_event { - -namespace { -TestEventFilter::HitsCounter* g_hits_counter; -} // namespace; - -// static -const char TestEventFilter::kName[] = "testing_predicate"; -bool TestEventFilter::filter_return_value_; - -// static -std::unique_ptr<TraceEventFilter> TestEventFilter::Factory( - const std::string& predicate_name) { - std::unique_ptr<TraceEventFilter> res; - if (predicate_name == kName) - res.reset(new TestEventFilter()); - return res; -} - -TestEventFilter::TestEventFilter() {} -TestEventFilter::~TestEventFilter() {} - -bool TestEventFilter::FilterTraceEvent(const TraceEvent& trace_event) const { - if (g_hits_counter) - g_hits_counter->filter_trace_event_hit_count++; - return filter_return_value_; -} - -void TestEventFilter::EndEvent(const char* category_name, - const char* name) const { - if (g_hits_counter) - g_hits_counter->end_event_hit_count++; -} - -TestEventFilter::HitsCounter::HitsCounter() { - Reset(); - DCHECK(!g_hits_counter); - g_hits_counter = this; -} - -TestEventFilter::HitsCounter::~HitsCounter() { - DCHECK(g_hits_counter); - g_hits_counter = nullptr; -} - -void TestEventFilter::HitsCounter::Reset() { - filter_trace_event_hit_count = 0; - end_event_hit_count = 0; -} - -} // namespace trace_event -} // namespace base diff --git a/base/trace_event/trace_event_filter_test_utils.h b/base/trace_event/trace_event_filter_test_utils.h deleted file mode 100644 index 419068b221..0000000000 --- a/base/trace_event/trace_event_filter_test_utils.h +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_TRACE_EVENT_TRACE_EVENT_FILTER_TEST_UTILS_H_ -#define BASE_TRACE_EVENT_TRACE_EVENT_FILTER_TEST_UTILS_H_ - -#include <memory> -#include <string> - -#include "base/macros.h" -#include "base/trace_event/trace_event_filter.h" - -namespace base { -namespace trace_event { - -class TestEventFilter : public TraceEventFilter { - public: - struct HitsCounter { - HitsCounter(); - ~HitsCounter(); - void Reset(); - size_t filter_trace_event_hit_count; - size_t end_event_hit_count; - }; - - static const char kName[]; - - // Factory method for TraceLog::SetFilterFactoryForTesting(). - static std::unique_ptr<TraceEventFilter> Factory( - const std::string& predicate_name); - - TestEventFilter(); - ~TestEventFilter() override; - - // TraceEventFilter implementation. - bool FilterTraceEvent(const TraceEvent& trace_event) const override; - void EndEvent(const char* category_name, const char* name) const override; - - static void set_filter_return_value(bool value) { - filter_return_value_ = value; - } - - private: - static bool filter_return_value_; - - DISALLOW_COPY_AND_ASSIGN(TestEventFilter); -}; - -} // namespace trace_event -} // namespace base - -#endif // BASE_TRACE_EVENT_TRACE_EVENT_FILTER_TEST_UTILS_H_ diff --git a/base/trace_event/trace_event_impl.cc b/base/trace_event/trace_event_impl.cc deleted file mode 100644 index cb23eb474c..0000000000 --- a/base/trace_event/trace_event_impl.cc +++ /dev/null @@ -1,490 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/trace_event/trace_event_impl.h" - -#include <stddef.h> - -#include "base/format_macros.h" -#include "base/json/string_escape.h" -#include "base/memory/ptr_util.h" -#include "base/process/process_handle.h" -#include "base/stl_util.h" -#include "base/strings/string_number_conversions.h" -#include "base/strings/string_util.h" -#include "base/strings/stringprintf.h" -#include "base/strings/utf_string_conversions.h" -#include "base/trace_event/trace_event.h" -#include "base/trace_event/trace_event_argument.h" -#include "base/trace_event/trace_log.h" - -namespace base { -namespace trace_event { - -namespace { - -size_t GetAllocLength(const char* str) { return str ? strlen(str) + 1 : 0; } - -// Copies |*member| into |*buffer|, sets |*member| to point to this new -// location, and then advances |*buffer| by the amount written. -void CopyTraceEventParameter(char** buffer, - const char** member, - const char* end) { - if (*member) { - size_t written = strlcpy(*buffer, *member, end - *buffer) + 1; - DCHECK_LE(static_cast<int>(written), end - *buffer); - *member = *buffer; - *buffer += written; - } -} - -} // namespace - -TraceEvent::TraceEvent() - : duration_(TimeDelta::FromInternalValue(-1)), - scope_(trace_event_internal::kGlobalScope), - id_(0u), - category_group_enabled_(NULL), - name_(NULL), - thread_id_(0), - flags_(0), - phase_(TRACE_EVENT_PHASE_BEGIN) { - for (int i = 0; i < kTraceMaxNumArgs; ++i) - arg_names_[i] = NULL; - memset(arg_values_, 0, sizeof(arg_values_)); -} - -TraceEvent::~TraceEvent() { -} - -void TraceEvent::MoveFrom(std::unique_ptr<TraceEvent> other) { - timestamp_ = other->timestamp_; - thread_timestamp_ = other->thread_timestamp_; - duration_ = other->duration_; - scope_ = other->scope_; - id_ = other->id_; - category_group_enabled_ = other->category_group_enabled_; - name_ = other->name_; - if (other->flags_ & TRACE_EVENT_FLAG_HAS_PROCESS_ID) - process_id_ = other->process_id_; - else - thread_id_ = other->thread_id_; - phase_ = other->phase_; - flags_ = other->flags_; - parameter_copy_storage_ = std::move(other->parameter_copy_storage_); - - for (int i = 0; i < kTraceMaxNumArgs; ++i) { - arg_names_[i] = other->arg_names_[i]; - arg_types_[i] = other->arg_types_[i]; - arg_values_[i] = other->arg_values_[i]; - convertable_values_[i] = std::move(other->convertable_values_[i]); - } -} - -void TraceEvent::Initialize( - int thread_id, - TimeTicks timestamp, - ThreadTicks thread_timestamp, - char phase, - const unsigned char* category_group_enabled, - const char* name, - const char* scope, - unsigned long long id, - unsigned long long bind_id, - int num_args, - const char** arg_names, - const unsigned char* arg_types, - const unsigned long long* arg_values, - std::unique_ptr<ConvertableToTraceFormat>* convertable_values, - unsigned int flags) { - timestamp_ = timestamp; - thread_timestamp_ = thread_timestamp; - duration_ = TimeDelta::FromInternalValue(-1); - scope_ = scope; - id_ = id; - category_group_enabled_ = category_group_enabled; - name_ = name; - thread_id_ = thread_id; - phase_ = phase; - flags_ = flags; - bind_id_ = bind_id; - - // Clamp num_args since it may have been set by a third_party library. - num_args = (num_args > kTraceMaxNumArgs) ? kTraceMaxNumArgs : num_args; - int i = 0; - for (; i < num_args; ++i) { - arg_names_[i] = arg_names[i]; - arg_types_[i] = arg_types[i]; - - if (arg_types[i] == TRACE_VALUE_TYPE_CONVERTABLE) { - convertable_values_[i] = std::move(convertable_values[i]); - } else { - arg_values_[i].as_uint = arg_values[i]; - convertable_values_[i].reset(); - } - } - for (; i < kTraceMaxNumArgs; ++i) { - arg_names_[i] = NULL; - arg_values_[i].as_uint = 0u; - convertable_values_[i].reset(); - arg_types_[i] = TRACE_VALUE_TYPE_UINT; - } - - bool copy = !!(flags & TRACE_EVENT_FLAG_COPY); - size_t alloc_size = 0; - if (copy) { - alloc_size += GetAllocLength(name) + GetAllocLength(scope); - for (i = 0; i < num_args; ++i) { - alloc_size += GetAllocLength(arg_names_[i]); - if (arg_types_[i] == TRACE_VALUE_TYPE_STRING) - arg_types_[i] = TRACE_VALUE_TYPE_COPY_STRING; - } - } - - bool arg_is_copy[kTraceMaxNumArgs]; - for (i = 0; i < num_args; ++i) { - // No copying of convertable types, we retain ownership. - if (arg_types_[i] == TRACE_VALUE_TYPE_CONVERTABLE) - continue; - - // We only take a copy of arg_vals if they are of type COPY_STRING. - arg_is_copy[i] = (arg_types_[i] == TRACE_VALUE_TYPE_COPY_STRING); - if (arg_is_copy[i]) - alloc_size += GetAllocLength(arg_values_[i].as_string); - } - - if (alloc_size) { - parameter_copy_storage_.reset(new std::string); - parameter_copy_storage_->resize(alloc_size); - char* ptr = string_as_array(parameter_copy_storage_.get()); - const char* end = ptr + alloc_size; - if (copy) { - CopyTraceEventParameter(&ptr, &name_, end); - CopyTraceEventParameter(&ptr, &scope_, end); - for (i = 0; i < num_args; ++i) { - CopyTraceEventParameter(&ptr, &arg_names_[i], end); - } - } - for (i = 0; i < num_args; ++i) { - if (arg_types_[i] == TRACE_VALUE_TYPE_CONVERTABLE) - continue; - if (arg_is_copy[i]) - CopyTraceEventParameter(&ptr, &arg_values_[i].as_string, end); - } - DCHECK_EQ(end, ptr) << "Overrun by " << ptr - end; - } -} - -void TraceEvent::Reset() { - // Only reset fields that won't be initialized in Initialize(), or that may - // hold references to other objects. - duration_ = TimeDelta::FromInternalValue(-1); - parameter_copy_storage_.reset(); - for (int i = 0; i < kTraceMaxNumArgs; ++i) - convertable_values_[i].reset(); -} - -void TraceEvent::UpdateDuration(const TimeTicks& now, - const ThreadTicks& thread_now) { - DCHECK_EQ(duration_.ToInternalValue(), -1); - duration_ = now - timestamp_; - - // |thread_timestamp_| can be empty if the thread ticks clock wasn't - // initialized when it was recorded. - if (thread_timestamp_ != ThreadTicks()) - thread_duration_ = thread_now - thread_timestamp_; -} - -void TraceEvent::EstimateTraceMemoryOverhead( - TraceEventMemoryOverhead* overhead) { - overhead->Add("TraceEvent", sizeof(*this)); - - if (parameter_copy_storage_) - overhead->AddString(*parameter_copy_storage_); - - for (size_t i = 0; i < kTraceMaxNumArgs; ++i) { - if (arg_types_[i] == TRACE_VALUE_TYPE_CONVERTABLE) - convertable_values_[i]->EstimateTraceMemoryOverhead(overhead); - } -} - -// static -void TraceEvent::AppendValueAsJSON(unsigned char type, - TraceEvent::TraceValue value, - std::string* out) { - switch (type) { - case TRACE_VALUE_TYPE_BOOL: - *out += value.as_bool ? "true" : "false"; - break; - case TRACE_VALUE_TYPE_UINT: - StringAppendF(out, "%" PRIu64, static_cast<uint64_t>(value.as_uint)); - break; - case TRACE_VALUE_TYPE_INT: - StringAppendF(out, "%" PRId64, static_cast<int64_t>(value.as_int)); - break; - case TRACE_VALUE_TYPE_DOUBLE: { - // FIXME: base/json/json_writer.cc is using the same code, - // should be made into a common method. - std::string real; - double val = value.as_double; - if (std::isfinite(val)) { - real = DoubleToString(val); - // Ensure that the number has a .0 if there's no decimal or 'e'. This - // makes sure that when we read the JSON back, it's interpreted as a - // real rather than an int. - if (real.find('.') == std::string::npos && - real.find('e') == std::string::npos && - real.find('E') == std::string::npos) { - real.append(".0"); - } - // The JSON spec requires that non-integer values in the range (-1,1) - // have a zero before the decimal point - ".52" is not valid, "0.52" is. - if (real[0] == '.') { - real.insert(0, "0"); - } else if (real.length() > 1 && real[0] == '-' && real[1] == '.') { - // "-.1" bad "-0.1" good - real.insert(1, "0"); - } - } else if (std::isnan(val)){ - // The JSON spec doesn't allow NaN and Infinity (since these are - // objects in EcmaScript). Use strings instead. - real = "\"NaN\""; - } else if (val < 0) { - real = "\"-Infinity\""; - } else { - real = "\"Infinity\""; - } - StringAppendF(out, "%s", real.c_str()); - break; - } - case TRACE_VALUE_TYPE_POINTER: - // JSON only supports double and int numbers. - // So as not to lose bits from a 64-bit pointer, output as a hex string. - StringAppendF( - out, "\"0x%" PRIx64 "\"", - static_cast<uint64_t>(reinterpret_cast<uintptr_t>(value.as_pointer))); - break; - case TRACE_VALUE_TYPE_STRING: - case TRACE_VALUE_TYPE_COPY_STRING: - EscapeJSONString(value.as_string ? value.as_string : "NULL", true, out); - break; - default: - NOTREACHED() << "Don't know how to print this value"; - break; - } -} - -void TraceEvent::AppendAsJSON( - std::string* out, - const ArgumentFilterPredicate& argument_filter_predicate) const { - int64_t time_int64 = timestamp_.ToInternalValue(); - int process_id; - int thread_id; - if ((flags_ & TRACE_EVENT_FLAG_HAS_PROCESS_ID) && - process_id_ != kNullProcessId) { - process_id = process_id_; - thread_id = -1; - } else { - process_id = TraceLog::GetInstance()->process_id(); - thread_id = thread_id_; - } - const char* category_group_name = - TraceLog::GetCategoryGroupName(category_group_enabled_); - - // Category group checked at category creation time. - DCHECK(!strchr(name_, '"')); - StringAppendF(out, "{\"pid\":%i,\"tid\":%i,\"ts\":%" PRId64 - ",\"ph\":\"%c\",\"cat\":\"%s\",\"name\":", - process_id, thread_id, time_int64, phase_, category_group_name); - EscapeJSONString(name_, true, out); - *out += ",\"args\":"; - - // Output argument names and values, stop at first NULL argument name. - // TODO(oysteine): The dual predicates here is a bit ugly; if the filtering - // capabilities need to grow even more precise we should rethink this - // approach - ArgumentNameFilterPredicate argument_name_filter_predicate; - bool strip_args = - arg_names_[0] && !argument_filter_predicate.is_null() && - !argument_filter_predicate.Run(category_group_name, name_, - &argument_name_filter_predicate); - - if (strip_args) { - *out += "\"__stripped__\""; - } else { - *out += "{"; - - for (int i = 0; i < kTraceMaxNumArgs && arg_names_[i]; ++i) { - if (i > 0) - *out += ","; - *out += "\""; - *out += arg_names_[i]; - *out += "\":"; - - if (argument_name_filter_predicate.is_null() || - argument_name_filter_predicate.Run(arg_names_[i])) { - if (arg_types_[i] == TRACE_VALUE_TYPE_CONVERTABLE) - convertable_values_[i]->AppendAsTraceFormat(out); - else - AppendValueAsJSON(arg_types_[i], arg_values_[i], out); - } else { - *out += "\"__stripped__\""; - } - } - - *out += "}"; - } - - if (phase_ == TRACE_EVENT_PHASE_COMPLETE) { - int64_t duration = duration_.ToInternalValue(); - if (duration != -1) - StringAppendF(out, ",\"dur\":%" PRId64, duration); - if (!thread_timestamp_.is_null()) { - int64_t thread_duration = thread_duration_.ToInternalValue(); - if (thread_duration != -1) - StringAppendF(out, ",\"tdur\":%" PRId64, thread_duration); - } - } - - // Output tts if thread_timestamp is valid. - if (!thread_timestamp_.is_null()) { - int64_t thread_time_int64 = thread_timestamp_.ToInternalValue(); - StringAppendF(out, ",\"tts\":%" PRId64, thread_time_int64); - } - - // Output async tts marker field if flag is set. - if (flags_ & TRACE_EVENT_FLAG_ASYNC_TTS) { - StringAppendF(out, ", \"use_async_tts\":1"); - } - - // If id_ is set, print it out as a hex string so we don't loose any - // bits (it might be a 64-bit pointer). - unsigned int id_flags_ = flags_ & (TRACE_EVENT_FLAG_HAS_ID | - TRACE_EVENT_FLAG_HAS_LOCAL_ID | - TRACE_EVENT_FLAG_HAS_GLOBAL_ID); - if (id_flags_) { - if (scope_ != trace_event_internal::kGlobalScope) - StringAppendF(out, ",\"scope\":\"%s\"", scope_); - - switch (id_flags_) { - case TRACE_EVENT_FLAG_HAS_ID: - StringAppendF(out, ",\"id\":\"0x%" PRIx64 "\"", - static_cast<uint64_t>(id_)); - break; - - case TRACE_EVENT_FLAG_HAS_LOCAL_ID: - StringAppendF(out, ",\"id2\":{\"local\":\"0x%" PRIx64 "\"}", - static_cast<uint64_t>(id_)); - break; - - case TRACE_EVENT_FLAG_HAS_GLOBAL_ID: - StringAppendF(out, ",\"id2\":{\"global\":\"0x%" PRIx64 "\"}", - static_cast<uint64_t>(id_)); - break; - - default: - NOTREACHED() << "More than one of the ID flags are set"; - break; - } - } - - if (flags_ & TRACE_EVENT_FLAG_BIND_TO_ENCLOSING) - StringAppendF(out, ",\"bp\":\"e\""); - - if ((flags_ & TRACE_EVENT_FLAG_FLOW_OUT) || - (flags_ & TRACE_EVENT_FLAG_FLOW_IN)) { - StringAppendF(out, ",\"bind_id\":\"0x%" PRIx64 "\"", - static_cast<uint64_t>(bind_id_)); - } - if (flags_ & TRACE_EVENT_FLAG_FLOW_IN) - StringAppendF(out, ",\"flow_in\":true"); - if (flags_ & TRACE_EVENT_FLAG_FLOW_OUT) - StringAppendF(out, ",\"flow_out\":true"); - - // Instant events also output their scope. - if (phase_ == TRACE_EVENT_PHASE_INSTANT) { - char scope = '?'; - switch (flags_ & TRACE_EVENT_FLAG_SCOPE_MASK) { - case TRACE_EVENT_SCOPE_GLOBAL: - scope = TRACE_EVENT_SCOPE_NAME_GLOBAL; - break; - - case TRACE_EVENT_SCOPE_PROCESS: - scope = TRACE_EVENT_SCOPE_NAME_PROCESS; - break; - - case TRACE_EVENT_SCOPE_THREAD: - scope = TRACE_EVENT_SCOPE_NAME_THREAD; - break; - } - StringAppendF(out, ",\"s\":\"%c\"", scope); - } - - *out += "}"; -} - -void TraceEvent::AppendPrettyPrinted(std::ostringstream* out) const { - *out << name_ << "["; - *out << TraceLog::GetCategoryGroupName(category_group_enabled_); - *out << "]"; - if (arg_names_[0]) { - *out << ", {"; - for (int i = 0; i < kTraceMaxNumArgs && arg_names_[i]; ++i) { - if (i > 0) - *out << ", "; - *out << arg_names_[i] << ":"; - std::string value_as_text; - - if (arg_types_[i] == TRACE_VALUE_TYPE_CONVERTABLE) - convertable_values_[i]->AppendAsTraceFormat(&value_as_text); - else - AppendValueAsJSON(arg_types_[i], arg_values_[i], &value_as_text); - - *out << value_as_text; - } - *out << "}"; - } -} - -} // namespace trace_event -} // namespace base - -namespace trace_event_internal { - -std::unique_ptr<base::trace_event::ConvertableToTraceFormat> -TraceID::AsConvertableToTraceFormat() const { - auto value = base::MakeUnique<base::trace_event::TracedValue>(); - - if (scope_ != kGlobalScope) - value->SetString("scope", scope_); - - const char* id_field_name = "id"; - if (id_flags_ == TRACE_EVENT_FLAG_HAS_GLOBAL_ID) { - id_field_name = "global"; - value->BeginDictionary("id2"); - } else if (id_flags_ == TRACE_EVENT_FLAG_HAS_LOCAL_ID) { - id_field_name = "local"; - value->BeginDictionary("id2"); - } else if (id_flags_ != TRACE_EVENT_FLAG_HAS_ID) { - NOTREACHED() << "Unrecognized ID flag"; - } - - if (has_prefix_) { - value->SetString(id_field_name, - base::StringPrintf("0x%" PRIx64 "/0x%" PRIx64, - static_cast<uint64_t>(prefix_), - static_cast<uint64_t>(raw_id_))); - } else { - value->SetString( - id_field_name, - base::StringPrintf("0x%" PRIx64, static_cast<uint64_t>(raw_id_))); - } - - if (id_flags_ != TRACE_EVENT_FLAG_HAS_ID) - value->EndDictionary(); - - return std::move(value); -} - -} // namespace trace_event_internal diff --git a/base/trace_event/trace_event_impl.h b/base/trace_event/trace_event_impl.h deleted file mode 100644 index 5eef702fb9..0000000000 --- a/base/trace_event/trace_event_impl.h +++ /dev/null @@ -1,184 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - - -#ifndef BASE_TRACE_EVENT_TRACE_EVENT_IMPL_H_ -#define BASE_TRACE_EVENT_TRACE_EVENT_IMPL_H_ - -#include <stdint.h> - -#include <memory> -#include <stack> -#include <string> -#include <vector> - -#include "base/atomicops.h" -#include "base/base_export.h" -#include "base/callback.h" -#include "base/containers/hash_tables.h" -#include "base/macros.h" -#include "base/observer_list.h" -#include "base/single_thread_task_runner.h" -#include "base/strings/string_util.h" -#include "base/synchronization/condition_variable.h" -#include "base/synchronization/lock.h" -#include "base/threading/thread_local.h" -#include "base/trace_event/trace_event_memory_overhead.h" -#include "build/build_config.h" - -namespace base { -namespace trace_event { - -typedef base::Callback<bool(const char* arg_name)> ArgumentNameFilterPredicate; - -typedef base::Callback<bool(const char* category_group_name, - const char* event_name, - ArgumentNameFilterPredicate*)> - ArgumentFilterPredicate; - -// For any argument of type TRACE_VALUE_TYPE_CONVERTABLE the provided -// class must implement this interface. -class BASE_EXPORT ConvertableToTraceFormat { - public: - ConvertableToTraceFormat() {} - virtual ~ConvertableToTraceFormat() {} - - // Append the class info to the provided |out| string. The appended - // data must be a valid JSON object. Strings must be properly quoted, and - // escaped. There is no processing applied to the content after it is - // appended. - virtual void AppendAsTraceFormat(std::string* out) const = 0; - - virtual void EstimateTraceMemoryOverhead(TraceEventMemoryOverhead* overhead); - - std::string ToString() const { - std::string result; - AppendAsTraceFormat(&result); - return result; - } - - private: - DISALLOW_COPY_AND_ASSIGN(ConvertableToTraceFormat); -}; - -const int kTraceMaxNumArgs = 2; - -struct TraceEventHandle { - uint32_t chunk_seq; - // These numbers of bits must be kept consistent with - // TraceBufferChunk::kMaxTrunkIndex and - // TraceBufferChunk::kTraceBufferChunkSize (in trace_buffer.h). - unsigned chunk_index : 26; - unsigned event_index : 6; -}; - -class BASE_EXPORT TraceEvent { - public: - union TraceValue { - bool as_bool; - unsigned long long as_uint; - long long as_int; - double as_double; - const void* as_pointer; - const char* as_string; - }; - - TraceEvent(); - ~TraceEvent(); - - void MoveFrom(std::unique_ptr<TraceEvent> other); - - void Initialize(int thread_id, - TimeTicks timestamp, - ThreadTicks thread_timestamp, - char phase, - const unsigned char* category_group_enabled, - const char* name, - const char* scope, - unsigned long long id, - unsigned long long bind_id, - int num_args, - const char** arg_names, - const unsigned char* arg_types, - const unsigned long long* arg_values, - std::unique_ptr<ConvertableToTraceFormat>* convertable_values, - unsigned int flags); - - void Reset(); - - void UpdateDuration(const TimeTicks& now, const ThreadTicks& thread_now); - - void EstimateTraceMemoryOverhead(TraceEventMemoryOverhead* overhead); - - // Serialize event data to JSON - void AppendAsJSON( - std::string* out, - const ArgumentFilterPredicate& argument_filter_predicate) const; - void AppendPrettyPrinted(std::ostringstream* out) const; - - static void AppendValueAsJSON(unsigned char type, - TraceValue value, - std::string* out); - - TimeTicks timestamp() const { return timestamp_; } - ThreadTicks thread_timestamp() const { return thread_timestamp_; } - char phase() const { return phase_; } - int thread_id() const { return thread_id_; } - TimeDelta duration() const { return duration_; } - TimeDelta thread_duration() const { return thread_duration_; } - const char* scope() const { return scope_; } - unsigned long long id() const { return id_; } - unsigned int flags() const { return flags_; } - - // Exposed for unittesting: - - const std::string* parameter_copy_storage() const { - return parameter_copy_storage_.get(); - } - - const unsigned char* category_group_enabled() const { - return category_group_enabled_; - } - - const char* name() const { return name_; } - -#if defined(OS_ANDROID) - void SendToATrace(); -#endif - - private: - // Note: these are ordered by size (largest first) for optimal packing. - TimeTicks timestamp_; - ThreadTicks thread_timestamp_; - TimeDelta duration_; - TimeDelta thread_duration_; - // scope_ and id_ can be used to store phase-specific data. - const char* scope_; - unsigned long long id_; - TraceValue arg_values_[kTraceMaxNumArgs]; - const char* arg_names_[kTraceMaxNumArgs]; - std::unique_ptr<ConvertableToTraceFormat> - convertable_values_[kTraceMaxNumArgs]; - const unsigned char* category_group_enabled_; - const char* name_; - std::unique_ptr<std::string> parameter_copy_storage_; - // Depending on TRACE_EVENT_FLAG_HAS_PROCESS_ID the event will have either: - // tid: thread_id_, pid: current_process_id (default case). - // tid: -1, pid: process_id_ (when flags_ & TRACE_EVENT_FLAG_HAS_PROCESS_ID). - union { - int thread_id_; - int process_id_; - }; - unsigned int flags_; - unsigned long long bind_id_; - unsigned char arg_types_[kTraceMaxNumArgs]; - char phase_; - - DISALLOW_COPY_AND_ASSIGN(TraceEvent); -}; - -} // namespace trace_event -} // namespace base - -#endif // BASE_TRACE_EVENT_TRACE_EVENT_IMPL_H_ diff --git a/base/trace_event/trace_event_memory_overhead.cc b/base/trace_event/trace_event_memory_overhead.cc deleted file mode 100644 index 8d56e1d80e..0000000000 --- a/base/trace_event/trace_event_memory_overhead.cc +++ /dev/null @@ -1,156 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/trace_event/trace_event_memory_overhead.h" - -#include <algorithm> - -#include "base/bits.h" -#include "base/memory/ref_counted_memory.h" -#include "base/strings/stringprintf.h" -#include "base/trace_event/memory_allocator_dump.h" -#include "base/trace_event/process_memory_dump.h" -#include "base/values.h" - -namespace base { -namespace trace_event { - -TraceEventMemoryOverhead::TraceEventMemoryOverhead() { -} - -TraceEventMemoryOverhead::~TraceEventMemoryOverhead() { -} - -void TraceEventMemoryOverhead::AddOrCreateInternal( - const char* object_type, - size_t count, - size_t allocated_size_in_bytes, - size_t resident_size_in_bytes) { - auto it = allocated_objects_.find(object_type); - if (it == allocated_objects_.end()) { - allocated_objects_.insert(std::make_pair( - object_type, - ObjectCountAndSize( - {count, allocated_size_in_bytes, resident_size_in_bytes}))); - return; - } - it->second.count += count; - it->second.allocated_size_in_bytes += allocated_size_in_bytes; - it->second.resident_size_in_bytes += resident_size_in_bytes; -} - -void TraceEventMemoryOverhead::Add(const char* object_type, - size_t allocated_size_in_bytes) { - Add(object_type, allocated_size_in_bytes, allocated_size_in_bytes); -} - -void TraceEventMemoryOverhead::Add(const char* object_type, - size_t allocated_size_in_bytes, - size_t resident_size_in_bytes) { - AddOrCreateInternal(object_type, 1, allocated_size_in_bytes, - resident_size_in_bytes); -} - -void TraceEventMemoryOverhead::AddString(const std::string& str) { - // The number below are empirical and mainly based on profiling of real-world - // std::string implementations: - // - even short string end up malloc()-inc at least 32 bytes. - // - longer strings seem to malloc() multiples of 16 bytes. - const size_t capacity = bits::Align(str.capacity(), 16); - Add("std::string", sizeof(std::string) + std::max<size_t>(capacity, 32u)); -} - -void TraceEventMemoryOverhead::AddRefCountedString( - const RefCountedString& str) { - Add("RefCountedString", sizeof(RefCountedString)); - AddString(str.data()); -} - -void TraceEventMemoryOverhead::AddValue(const Value& value) { - switch (value.GetType()) { - case Value::Type::NONE: - case Value::Type::BOOLEAN: - case Value::Type::INTEGER: - case Value::Type::DOUBLE: - Add("FundamentalValue", sizeof(Value)); - break; - - case Value::Type::STRING: { - const Value* string_value = nullptr; - value.GetAsString(&string_value); - Add("StringValue", sizeof(Value)); - AddString(string_value->GetString()); - } break; - - case Value::Type::BINARY: { - const BinaryValue* binary_value = nullptr; - value.GetAsBinary(&binary_value); - Add("BinaryValue", sizeof(BinaryValue) + binary_value->GetSize()); - } break; - - case Value::Type::DICTIONARY: { - const DictionaryValue* dictionary_value = nullptr; - value.GetAsDictionary(&dictionary_value); - Add("DictionaryValue", sizeof(DictionaryValue)); - for (DictionaryValue::Iterator it(*dictionary_value); !it.IsAtEnd(); - it.Advance()) { - AddString(it.key()); - AddValue(it.value()); - } - } break; - - case Value::Type::LIST: { - const ListValue* list_value = nullptr; - value.GetAsList(&list_value); - Add("ListValue", sizeof(ListValue)); - for (const auto& v : *list_value) - AddValue(*v); - } break; - - default: - NOTREACHED(); - } -} - -void TraceEventMemoryOverhead::AddSelf() { - size_t estimated_size = sizeof(*this); - // If the SmallMap did overflow its static capacity, its elements will be - // allocated on the heap and have to be accounted separately. - if (allocated_objects_.UsingFullMap()) - estimated_size += sizeof(map_type::value_type) * allocated_objects_.size(); - Add("TraceEventMemoryOverhead", estimated_size); -} - -size_t TraceEventMemoryOverhead::GetCount(const char* object_type) const { - const auto& it = allocated_objects_.find(object_type); - if (it == allocated_objects_.end()) - return 0u; - return it->second.count; -} - -void TraceEventMemoryOverhead::Update(const TraceEventMemoryOverhead& other) { - for (const auto& it : other.allocated_objects_) { - AddOrCreateInternal(it.first, it.second.count, - it.second.allocated_size_in_bytes, - it.second.resident_size_in_bytes); - } -} - -void TraceEventMemoryOverhead::DumpInto(const char* base_name, - ProcessMemoryDump* pmd) const { - for (const auto& it : allocated_objects_) { - std::string dump_name = StringPrintf("%s/%s", base_name, it.first); - MemoryAllocatorDump* mad = pmd->CreateAllocatorDump(dump_name); - mad->AddScalar(MemoryAllocatorDump::kNameSize, - MemoryAllocatorDump::kUnitsBytes, - it.second.allocated_size_in_bytes); - mad->AddScalar("resident_size", MemoryAllocatorDump::kUnitsBytes, - it.second.resident_size_in_bytes); - mad->AddScalar(MemoryAllocatorDump::kNameObjectCount, - MemoryAllocatorDump::kUnitsObjects, it.second.count); - } -} - -} // namespace trace_event -} // namespace base diff --git a/base/trace_event/trace_event_memory_overhead.h b/base/trace_event/trace_event_memory_overhead.h deleted file mode 100644 index a69c93fed2..0000000000 --- a/base/trace_event/trace_event_memory_overhead.h +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_TRACE_EVENT_TRACE_EVENT_MEMORY_OVERHEAD_H_ -#define BASE_TRACE_EVENT_TRACE_EVENT_MEMORY_OVERHEAD_H_ - -#include <stddef.h> - -#include "base/base_export.h" -#include "base/containers/hash_tables.h" -#include "base/containers/small_map.h" -#include "base/macros.h" - -namespace base { - -class RefCountedString; -class Value; - -namespace trace_event { - -class ProcessMemoryDump; - -// Used to estimate the memory overhead of the tracing infrastructure. -class BASE_EXPORT TraceEventMemoryOverhead { - public: - TraceEventMemoryOverhead(); - ~TraceEventMemoryOverhead(); - - // Use this method to account the overhead of an object for which an estimate - // is known for both the allocated and resident memory. - void Add(const char* object_type, - size_t allocated_size_in_bytes, - size_t resident_size_in_bytes); - - // Similar to Add() above, but assumes that - // |resident_size_in_bytes| == |allocated_size_in_bytes|. - void Add(const char* object_type, size_t allocated_size_in_bytes); - - // Specialized profiling functions for commonly used object types. - void AddString(const std::string& str); - void AddValue(const Value& value); - void AddRefCountedString(const RefCountedString& str); - - // Call this after all the Add* methods above to account the memory used by - // this TraceEventMemoryOverhead instance itself. - void AddSelf(); - - // Retrieves the count, that is, the count of Add*(|object_type|, ...) calls. - size_t GetCount(const char* object_type) const; - - // Adds up and merges all the values from |other| to this instance. - void Update(const TraceEventMemoryOverhead& other); - - void DumpInto(const char* base_name, ProcessMemoryDump* pmd) const; - - private: - struct ObjectCountAndSize { - size_t count; - size_t allocated_size_in_bytes; - size_t resident_size_in_bytes; - }; - using map_type = SmallMap<hash_map<const char*, ObjectCountAndSize>, 16>; - map_type allocated_objects_; - - void AddOrCreateInternal(const char* object_type, - size_t count, - size_t allocated_size_in_bytes, - size_t resident_size_in_bytes); - - DISALLOW_COPY_AND_ASSIGN(TraceEventMemoryOverhead); -}; - -} // namespace trace_event -} // namespace base - -#endif // BASE_TRACE_EVENT_TRACE_EVENT_MEMORY_OVERHEAD_H_ diff --git a/base/trace_event/trace_event_synthetic_delay.cc b/base/trace_event/trace_event_synthetic_delay.cc deleted file mode 100644 index cfae7435e9..0000000000 --- a/base/trace_event/trace_event_synthetic_delay.cc +++ /dev/null @@ -1,235 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/macros.h" -#include "base/memory/singleton.h" -#include "base/third_party/dynamic_annotations/dynamic_annotations.h" -#include "base/trace_event/trace_event_synthetic_delay.h" - -namespace { -const int kMaxSyntheticDelays = 32; -} // namespace - -namespace base { -namespace trace_event { - -TraceEventSyntheticDelayClock::TraceEventSyntheticDelayClock() {} -TraceEventSyntheticDelayClock::~TraceEventSyntheticDelayClock() {} - -class TraceEventSyntheticDelayRegistry : public TraceEventSyntheticDelayClock { - public: - static TraceEventSyntheticDelayRegistry* GetInstance(); - - TraceEventSyntheticDelay* GetOrCreateDelay(const char* name); - void ResetAllDelays(); - - // TraceEventSyntheticDelayClock implementation. - TimeTicks Now() override; - - private: - TraceEventSyntheticDelayRegistry(); - - friend struct DefaultSingletonTraits<TraceEventSyntheticDelayRegistry>; - - Lock lock_; - TraceEventSyntheticDelay delays_[kMaxSyntheticDelays]; - TraceEventSyntheticDelay dummy_delay_; - subtle::Atomic32 delay_count_; - - DISALLOW_COPY_AND_ASSIGN(TraceEventSyntheticDelayRegistry); -}; - -TraceEventSyntheticDelay::TraceEventSyntheticDelay() - : mode_(STATIC), begin_count_(0), trigger_count_(0), clock_(NULL) {} - -TraceEventSyntheticDelay::~TraceEventSyntheticDelay() {} - -TraceEventSyntheticDelay* TraceEventSyntheticDelay::Lookup( - const std::string& name) { - return TraceEventSyntheticDelayRegistry::GetInstance()->GetOrCreateDelay( - name.c_str()); -} - -void TraceEventSyntheticDelay::Initialize( - const std::string& name, - TraceEventSyntheticDelayClock* clock) { - name_ = name; - clock_ = clock; -} - -void TraceEventSyntheticDelay::SetTargetDuration(TimeDelta target_duration) { - AutoLock lock(lock_); - target_duration_ = target_duration; - trigger_count_ = 0; - begin_count_ = 0; -} - -void TraceEventSyntheticDelay::SetMode(Mode mode) { - AutoLock lock(lock_); - mode_ = mode; -} - -void TraceEventSyntheticDelay::SetClock(TraceEventSyntheticDelayClock* clock) { - AutoLock lock(lock_); - clock_ = clock; -} - -void TraceEventSyntheticDelay::Begin() { - // Note that we check for a non-zero target duration without locking to keep - // things quick for the common case when delays are disabled. Since the delay - // calculation is done with a lock held, it will always be correct. The only - // downside of this is that we may fail to apply some delays when the target - // duration changes. - ANNOTATE_BENIGN_RACE(&target_duration_, "Synthetic delay duration"); - if (!target_duration_.ToInternalValue()) - return; - - TimeTicks start_time = clock_->Now(); - { - AutoLock lock(lock_); - if (++begin_count_ != 1) - return; - end_time_ = CalculateEndTimeLocked(start_time); - } -} - -void TraceEventSyntheticDelay::BeginParallel(TimeTicks* out_end_time) { - // See note in Begin(). - ANNOTATE_BENIGN_RACE(&target_duration_, "Synthetic delay duration"); - if (!target_duration_.ToInternalValue()) { - *out_end_time = TimeTicks(); - return; - } - - TimeTicks start_time = clock_->Now(); - { - AutoLock lock(lock_); - *out_end_time = CalculateEndTimeLocked(start_time); - } -} - -void TraceEventSyntheticDelay::End() { - // See note in Begin(). - ANNOTATE_BENIGN_RACE(&target_duration_, "Synthetic delay duration"); - if (!target_duration_.ToInternalValue()) - return; - - TimeTicks end_time; - { - AutoLock lock(lock_); - if (!begin_count_ || --begin_count_ != 0) - return; - end_time = end_time_; - } - if (!end_time.is_null()) - ApplyDelay(end_time); -} - -void TraceEventSyntheticDelay::EndParallel(TimeTicks end_time) { - if (!end_time.is_null()) - ApplyDelay(end_time); -} - -TimeTicks TraceEventSyntheticDelay::CalculateEndTimeLocked( - TimeTicks start_time) { - if (mode_ == ONE_SHOT && trigger_count_++) - return TimeTicks(); - else if (mode_ == ALTERNATING && trigger_count_++ % 2) - return TimeTicks(); - return start_time + target_duration_; -} - -void TraceEventSyntheticDelay::ApplyDelay(TimeTicks end_time) { - TRACE_EVENT0("synthetic_delay", name_.c_str()); - while (clock_->Now() < end_time) { - // Busy loop. - } -} - -TraceEventSyntheticDelayRegistry* -TraceEventSyntheticDelayRegistry::GetInstance() { - return Singleton< - TraceEventSyntheticDelayRegistry, - LeakySingletonTraits<TraceEventSyntheticDelayRegistry> >::get(); -} - -TraceEventSyntheticDelayRegistry::TraceEventSyntheticDelayRegistry() - : delay_count_(0) {} - -TraceEventSyntheticDelay* TraceEventSyntheticDelayRegistry::GetOrCreateDelay( - const char* name) { - // Try to find an existing delay first without locking to make the common case - // fast. - int delay_count = subtle::Acquire_Load(&delay_count_); - for (int i = 0; i < delay_count; ++i) { - if (!strcmp(name, delays_[i].name_.c_str())) - return &delays_[i]; - } - - AutoLock lock(lock_); - delay_count = subtle::Acquire_Load(&delay_count_); - for (int i = 0; i < delay_count; ++i) { - if (!strcmp(name, delays_[i].name_.c_str())) - return &delays_[i]; - } - - DCHECK(delay_count < kMaxSyntheticDelays) - << "must increase kMaxSyntheticDelays"; - if (delay_count >= kMaxSyntheticDelays) - return &dummy_delay_; - - delays_[delay_count].Initialize(std::string(name), this); - subtle::Release_Store(&delay_count_, delay_count + 1); - return &delays_[delay_count]; -} - -TimeTicks TraceEventSyntheticDelayRegistry::Now() { - return TimeTicks::Now(); -} - -void TraceEventSyntheticDelayRegistry::ResetAllDelays() { - AutoLock lock(lock_); - int delay_count = subtle::Acquire_Load(&delay_count_); - for (int i = 0; i < delay_count; ++i) { - delays_[i].SetTargetDuration(TimeDelta()); - delays_[i].SetClock(this); - } -} - -void ResetTraceEventSyntheticDelays() { - TraceEventSyntheticDelayRegistry::GetInstance()->ResetAllDelays(); -} - -} // namespace trace_event -} // namespace base - -namespace trace_event_internal { - -ScopedSyntheticDelay::ScopedSyntheticDelay(const char* name, - base::subtle::AtomicWord* impl_ptr) - : delay_impl_(GetOrCreateDelay(name, impl_ptr)) { - delay_impl_->BeginParallel(&end_time_); -} - -ScopedSyntheticDelay::~ScopedSyntheticDelay() { - delay_impl_->EndParallel(end_time_); -} - -base::trace_event::TraceEventSyntheticDelay* GetOrCreateDelay( - const char* name, - base::subtle::AtomicWord* impl_ptr) { - base::trace_event::TraceEventSyntheticDelay* delay_impl = - reinterpret_cast<base::trace_event::TraceEventSyntheticDelay*>( - base::subtle::Acquire_Load(impl_ptr)); - if (!delay_impl) { - delay_impl = - base::trace_event::TraceEventSyntheticDelayRegistry::GetInstance() - ->GetOrCreateDelay(name); - base::subtle::Release_Store( - impl_ptr, reinterpret_cast<base::subtle::AtomicWord>(delay_impl)); - } - return delay_impl; -} - -} // namespace trace_event_internal diff --git a/base/trace_event/trace_event_synthetic_delay.h b/base/trace_event/trace_event_synthetic_delay.h deleted file mode 100644 index e86f9eee2c..0000000000 --- a/base/trace_event/trace_event_synthetic_delay.h +++ /dev/null @@ -1,164 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// The synthetic delay framework makes it possible to dynamically inject -// arbitrary delays into into different parts of the codebase. This can be used, -// for instance, for testing various task scheduling algorithms. -// -// The delays are specified in terms of a target duration for a given block of -// code. If the code executes faster than the duration, the thread is made to -// sleep until the deadline is met. -// -// Code can be instrumented for delays with two sets of macros. First, for -// delays that should apply within a scope, use the following macro: -// -// TRACE_EVENT_SYNTHETIC_DELAY("cc.LayerTreeHost.DrawAndSwap"); -// -// For delaying operations that span multiple scopes, use: -// -// TRACE_EVENT_SYNTHETIC_DELAY_BEGIN("cc.Scheduler.BeginMainFrame"); -// ... -// TRACE_EVENT_SYNTHETIC_DELAY_END("cc.Scheduler.BeginMainFrame"); -// -// Here BEGIN establishes the start time for the delay and END executes the -// delay based on the remaining time. If BEGIN is called multiple times in a -// row, END should be called a corresponding number of times. Only the last -// call to END will have an effect. -// -// Note that a single delay may begin on one thread and end on another. This -// implies that a single delay cannot not be applied in several threads at once. - -#ifndef BASE_TRACE_EVENT_TRACE_EVENT_SYNTHETIC_DELAY_H_ -#define BASE_TRACE_EVENT_TRACE_EVENT_SYNTHETIC_DELAY_H_ - -#include "base/atomicops.h" -#include "base/macros.h" -#include "base/synchronization/lock.h" -#include "base/time/time.h" -#include "base/trace_event/trace_event.h" - -// Apply a named delay in the current scope. -#define TRACE_EVENT_SYNTHETIC_DELAY(name) \ - static base::subtle::AtomicWord INTERNAL_TRACE_EVENT_UID(impl_ptr) = 0; \ - trace_event_internal::ScopedSyntheticDelay INTERNAL_TRACE_EVENT_UID(delay)( \ - name, &INTERNAL_TRACE_EVENT_UID(impl_ptr)); - -// Begin a named delay, establishing its timing start point. May be called -// multiple times as long as the calls to TRACE_EVENT_SYNTHETIC_DELAY_END are -// balanced. Only the first call records the timing start point. -#define TRACE_EVENT_SYNTHETIC_DELAY_BEGIN(name) \ - do { \ - static base::subtle::AtomicWord impl_ptr = 0; \ - trace_event_internal::GetOrCreateDelay(name, &impl_ptr)->Begin(); \ - } while (false) - -// End a named delay. The delay is applied only if this call matches the -// first corresponding call to TRACE_EVENT_SYNTHETIC_DELAY_BEGIN with the -// same delay. -#define TRACE_EVENT_SYNTHETIC_DELAY_END(name) \ - do { \ - static base::subtle::AtomicWord impl_ptr = 0; \ - trace_event_internal::GetOrCreateDelay(name, &impl_ptr)->End(); \ - } while (false) - -namespace base { -namespace trace_event { - -// Time source for computing delay durations. Used for testing. -class TRACE_EVENT_API_CLASS_EXPORT TraceEventSyntheticDelayClock { - public: - TraceEventSyntheticDelayClock(); - virtual ~TraceEventSyntheticDelayClock(); - virtual base::TimeTicks Now() = 0; - - private: - DISALLOW_COPY_AND_ASSIGN(TraceEventSyntheticDelayClock); -}; - -// Single delay point instance. -class TRACE_EVENT_API_CLASS_EXPORT TraceEventSyntheticDelay { - public: - enum Mode { - STATIC, // Apply the configured delay every time. - ONE_SHOT, // Apply the configured delay just once. - ALTERNATING // Apply the configured delay every other time. - }; - - // Returns an existing named delay instance or creates a new one with |name|. - static TraceEventSyntheticDelay* Lookup(const std::string& name); - - void SetTargetDuration(TimeDelta target_duration); - void SetMode(Mode mode); - void SetClock(TraceEventSyntheticDelayClock* clock); - - // Begin the delay, establishing its timing start point. May be called - // multiple times as long as the calls to End() are balanced. Only the first - // call records the timing start point. - void Begin(); - - // End the delay. The delay is applied only if this call matches the first - // corresponding call to Begin() with the same delay. - void End(); - - // Begin a parallel instance of the delay. Several parallel instances may be - // active simultaneously and will complete independently. The computed end - // time for the delay is stored in |out_end_time|, which should later be - // passed to EndParallel(). - void BeginParallel(base::TimeTicks* out_end_time); - - // End a previously started parallel delay. |end_time| is the delay end point - // computed by BeginParallel(). - void EndParallel(base::TimeTicks end_time); - - private: - TraceEventSyntheticDelay(); - ~TraceEventSyntheticDelay(); - friend class TraceEventSyntheticDelayRegistry; - - void Initialize(const std::string& name, - TraceEventSyntheticDelayClock* clock); - base::TimeTicks CalculateEndTimeLocked(base::TimeTicks start_time); - void ApplyDelay(base::TimeTicks end_time); - - Lock lock_; - Mode mode_; - std::string name_; - int begin_count_; - int trigger_count_; - base::TimeTicks end_time_; - base::TimeDelta target_duration_; - TraceEventSyntheticDelayClock* clock_; - - DISALLOW_COPY_AND_ASSIGN(TraceEventSyntheticDelay); -}; - -// Set the target durations of all registered synthetic delay points to zero. -TRACE_EVENT_API_CLASS_EXPORT void ResetTraceEventSyntheticDelays(); - -} // namespace trace_event -} // namespace base - -namespace trace_event_internal { - -// Helper class for scoped delays. Do not use directly. -class TRACE_EVENT_API_CLASS_EXPORT ScopedSyntheticDelay { - public: - explicit ScopedSyntheticDelay(const char* name, - base::subtle::AtomicWord* impl_ptr); - ~ScopedSyntheticDelay(); - - private: - base::trace_event::TraceEventSyntheticDelay* delay_impl_; - base::TimeTicks end_time_; - - DISALLOW_COPY_AND_ASSIGN(ScopedSyntheticDelay); -}; - -// Helper for registering delays. Do not use directly. -TRACE_EVENT_API_CLASS_EXPORT base::trace_event::TraceEventSyntheticDelay* - GetOrCreateDelay(const char* name, base::subtle::AtomicWord* impl_ptr); - -} // namespace trace_event_internal - -#endif // BASE_TRACE_EVENT_TRACE_EVENT_SYNTHETIC_DELAY_H_ diff --git a/base/trace_event/trace_event_synthetic_delay_unittest.cc b/base/trace_event/trace_event_synthetic_delay_unittest.cc deleted file mode 100644 index 97a4580b3b..0000000000 --- a/base/trace_event/trace_event_synthetic_delay_unittest.cc +++ /dev/null @@ -1,157 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/trace_event/trace_event_synthetic_delay.h" - -#include <stdint.h> - -#include "base/macros.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { -namespace trace_event { -namespace { - -const int kTargetDurationMs = 100; -// Allow some leeway in timings to make it possible to run these tests with a -// wall clock time source too. -const int kShortDurationMs = 10; - -} // namespace - -class TraceEventSyntheticDelayTest : public testing::Test, - public TraceEventSyntheticDelayClock { - public: - TraceEventSyntheticDelayTest() {} - ~TraceEventSyntheticDelayTest() override { ResetTraceEventSyntheticDelays(); } - - // TraceEventSyntheticDelayClock implementation. - base::TimeTicks Now() override { - AdvanceTime(base::TimeDelta::FromMilliseconds(kShortDurationMs / 10)); - return now_; - } - - TraceEventSyntheticDelay* ConfigureDelay(const char* name) { - TraceEventSyntheticDelay* delay = TraceEventSyntheticDelay::Lookup(name); - delay->SetClock(this); - delay->SetTargetDuration( - base::TimeDelta::FromMilliseconds(kTargetDurationMs)); - return delay; - } - - void AdvanceTime(base::TimeDelta delta) { now_ += delta; } - - int64_t TestFunction() { - base::TimeTicks start = Now(); - { TRACE_EVENT_SYNTHETIC_DELAY("test.Delay"); } - return (Now() - start).InMilliseconds(); - } - - int64_t AsyncTestFunctionBegin() { - base::TimeTicks start = Now(); - { TRACE_EVENT_SYNTHETIC_DELAY_BEGIN("test.AsyncDelay"); } - return (Now() - start).InMilliseconds(); - } - - int64_t AsyncTestFunctionEnd() { - base::TimeTicks start = Now(); - { TRACE_EVENT_SYNTHETIC_DELAY_END("test.AsyncDelay"); } - return (Now() - start).InMilliseconds(); - } - - private: - base::TimeTicks now_; - - DISALLOW_COPY_AND_ASSIGN(TraceEventSyntheticDelayTest); -}; - -TEST_F(TraceEventSyntheticDelayTest, StaticDelay) { - TraceEventSyntheticDelay* delay = ConfigureDelay("test.Delay"); - delay->SetMode(TraceEventSyntheticDelay::STATIC); - EXPECT_GE(TestFunction(), kTargetDurationMs); -} - -TEST_F(TraceEventSyntheticDelayTest, OneShotDelay) { - TraceEventSyntheticDelay* delay = ConfigureDelay("test.Delay"); - delay->SetMode(TraceEventSyntheticDelay::ONE_SHOT); - EXPECT_GE(TestFunction(), kTargetDurationMs); - EXPECT_LT(TestFunction(), kShortDurationMs); - - delay->SetTargetDuration( - base::TimeDelta::FromMilliseconds(kTargetDurationMs)); - EXPECT_GE(TestFunction(), kTargetDurationMs); -} - -TEST_F(TraceEventSyntheticDelayTest, AlternatingDelay) { - TraceEventSyntheticDelay* delay = ConfigureDelay("test.Delay"); - delay->SetMode(TraceEventSyntheticDelay::ALTERNATING); - EXPECT_GE(TestFunction(), kTargetDurationMs); - EXPECT_LT(TestFunction(), kShortDurationMs); - EXPECT_GE(TestFunction(), kTargetDurationMs); - EXPECT_LT(TestFunction(), kShortDurationMs); -} - -TEST_F(TraceEventSyntheticDelayTest, AsyncDelay) { - ConfigureDelay("test.AsyncDelay"); - EXPECT_LT(AsyncTestFunctionBegin(), kShortDurationMs); - EXPECT_GE(AsyncTestFunctionEnd(), kTargetDurationMs / 2); -} - -TEST_F(TraceEventSyntheticDelayTest, AsyncDelayExceeded) { - ConfigureDelay("test.AsyncDelay"); - EXPECT_LT(AsyncTestFunctionBegin(), kShortDurationMs); - AdvanceTime(base::TimeDelta::FromMilliseconds(kTargetDurationMs)); - EXPECT_LT(AsyncTestFunctionEnd(), kShortDurationMs); -} - -TEST_F(TraceEventSyntheticDelayTest, AsyncDelayNoActivation) { - ConfigureDelay("test.AsyncDelay"); - EXPECT_LT(AsyncTestFunctionEnd(), kShortDurationMs); -} - -TEST_F(TraceEventSyntheticDelayTest, AsyncDelayNested) { - ConfigureDelay("test.AsyncDelay"); - EXPECT_LT(AsyncTestFunctionBegin(), kShortDurationMs); - EXPECT_LT(AsyncTestFunctionBegin(), kShortDurationMs); - EXPECT_LT(AsyncTestFunctionEnd(), kShortDurationMs); - EXPECT_GE(AsyncTestFunctionEnd(), kTargetDurationMs / 2); -} - -TEST_F(TraceEventSyntheticDelayTest, AsyncDelayUnbalanced) { - ConfigureDelay("test.AsyncDelay"); - EXPECT_LT(AsyncTestFunctionBegin(), kShortDurationMs); - EXPECT_GE(AsyncTestFunctionEnd(), kTargetDurationMs / 2); - EXPECT_LT(AsyncTestFunctionEnd(), kShortDurationMs); - - EXPECT_LT(AsyncTestFunctionBegin(), kShortDurationMs); - EXPECT_GE(AsyncTestFunctionEnd(), kTargetDurationMs / 2); -} - -TEST_F(TraceEventSyntheticDelayTest, ResetDelays) { - ConfigureDelay("test.Delay"); - ResetTraceEventSyntheticDelays(); - EXPECT_LT(TestFunction(), kShortDurationMs); -} - -TEST_F(TraceEventSyntheticDelayTest, BeginParallel) { - TraceEventSyntheticDelay* delay = ConfigureDelay("test.AsyncDelay"); - base::TimeTicks end_times[2]; - base::TimeTicks start_time = Now(); - - delay->BeginParallel(&end_times[0]); - EXPECT_FALSE(end_times[0].is_null()); - - delay->BeginParallel(&end_times[1]); - EXPECT_FALSE(end_times[1].is_null()); - - delay->EndParallel(end_times[0]); - EXPECT_GE((Now() - start_time).InMilliseconds(), kTargetDurationMs); - - start_time = Now(); - delay->EndParallel(end_times[1]); - EXPECT_LT((Now() - start_time).InMilliseconds(), kShortDurationMs); -} - -} // namespace trace_event -} // namespace base diff --git a/base/trace_event/trace_event_system_stats_monitor.h b/base/trace_event/trace_event_system_stats_monitor.h deleted file mode 100644 index 14aa5681fe..0000000000 --- a/base/trace_event/trace_event_system_stats_monitor.h +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_TRACE_EVENT_TRACE_EVENT_SYSTEM_STATS_MONITOR_H_ -#define BASE_TRACE_EVENT_TRACE_EVENT_SYSTEM_STATS_MONITOR_H_ - -#include "base/base_export.h" -#include "base/gtest_prod_util.h" -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "base/memory/weak_ptr.h" -#include "base/process/process_metrics.h" -#include "base/timer/timer.h" -#include "base/trace_event/trace_log.h" - -namespace base { - -class SingleThreadTaskRunner; - -namespace trace_event { - -// Watches for chrome://tracing to be enabled or disabled. When tracing is -// enabled, also enables system events profiling. This class is the preferred -// way to turn system tracing on and off. -class BASE_EXPORT TraceEventSystemStatsMonitor - : public TraceLog::EnabledStateObserver { - public: - // Length of time interval between stat profiles. - static const int kSamplingIntervalMilliseconds = 2000; - - // |task_runner| must be the primary thread for the client - // process, e.g. the UI thread in a browser. - explicit TraceEventSystemStatsMonitor( - scoped_refptr<SingleThreadTaskRunner> task_runner); - - ~TraceEventSystemStatsMonitor() override; - - // base::trace_event::TraceLog::EnabledStateChangedObserver overrides: - void OnTraceLogEnabled() override; - void OnTraceLogDisabled() override; - - // Retrieves system profiling at the current time. - void DumpSystemStats(); - - private: - FRIEND_TEST_ALL_PREFIXES(TraceSystemStatsMonitorTest, - TraceEventSystemStatsMonitor); - - bool IsTimerRunningForTest() const; - - void StartProfiling(); - - void StopProfiling(); - - // Ensures the observer starts and stops tracing on the primary thread. - scoped_refptr<SingleThreadTaskRunner> task_runner_; - - // Timer to schedule system profile dumps. - RepeatingTimer dump_timer_; - - WeakPtrFactory<TraceEventSystemStatsMonitor> weak_factory_; - - DISALLOW_COPY_AND_ASSIGN(TraceEventSystemStatsMonitor); -}; - -// Converts system memory profiling stats in |input| to -// trace event compatible JSON and appends to |output|. Visible for testing. -BASE_EXPORT void AppendSystemProfileAsTraceFormat(const SystemMetrics& - system_stats, - std::string* output); - -} // namespace trace_event -} // namespace base - -#endif // BASE_TRACE_EVENT_TRACE_EVENT_SYSTEM_STATS_MONITOR_H_ diff --git a/base/trace_event/trace_event_unittest.cc b/base/trace_event/trace_event_unittest.cc deleted file mode 100644 index 7a30e4ee57..0000000000 --- a/base/trace_event/trace_event_unittest.cc +++ /dev/null @@ -1,3220 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/trace_event/trace_event.h" - -#include <math.h> -#include <stddef.h> -#include <stdint.h> - -#include <cstdlib> -#include <memory> -#include <utility> - -#include "base/bind.h" -#include "base/command_line.h" -#include "base/json/json_reader.h" -#include "base/json/json_writer.h" -#include "base/location.h" -#include "base/macros.h" -#include "base/memory/ptr_util.h" -#include "base/memory/ref_counted_memory.h" -#include "base/memory/singleton.h" -#include "base/process/process_handle.h" -#include "base/single_thread_task_runner.h" -#include "base/stl_util.h" -#include "base/strings/pattern.h" -#include "base/strings/stringprintf.h" -#include "base/synchronization/waitable_event.h" -#include "base/threading/platform_thread.h" -#include "base/threading/thread.h" -#include "base/time/time.h" -#include "base/trace_event/event_name_filter.h" -#include "base/trace_event/heap_profiler_event_filter.h" -#include "base/trace_event/trace_buffer.h" -#include "base/trace_event/trace_event.h" -#include "base/trace_event/trace_event_filter.h" -#include "base/trace_event/trace_event_filter_test_utils.h" -#include "base/trace_event/trace_event_synthetic_delay.h" -#include "base/values.h" -#include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { -namespace trace_event { - -namespace { - -enum CompareOp { - IS_EQUAL, - IS_NOT_EQUAL, -}; - -struct JsonKeyValue { - const char* key; - const char* value; - CompareOp op; -}; - -const int kThreadId = 42; -const int kAsyncId = 5; -const char kAsyncIdStr[] = "0x5"; -const int kAsyncId2 = 6; -const char kAsyncId2Str[] = "0x6"; -const int kFlowId = 7; -const char kFlowIdStr[] = "0x7"; - -const char kRecordAllCategoryFilter[] = "*"; - -class TraceEventTestFixture : public testing::Test { - public: - void OnTraceDataCollected( - WaitableEvent* flush_complete_event, - const scoped_refptr<base::RefCountedString>& events_str, - bool has_more_events); - DictionaryValue* FindMatchingTraceEntry(const JsonKeyValue* key_values); - DictionaryValue* FindNamePhase(const char* name, const char* phase); - DictionaryValue* FindNamePhaseKeyValue(const char* name, - const char* phase, - const char* key, - const char* value); - void DropTracedMetadataRecords(); - bool FindMatchingValue(const char* key, - const char* value); - bool FindNonMatchingValue(const char* key, - const char* value); - void Clear() { - trace_parsed_.Clear(); - json_output_.json_output.clear(); - } - - void BeginTrace() { - BeginSpecificTrace("*"); - } - - void BeginSpecificTrace(const std::string& filter) { - TraceLog::GetInstance()->SetEnabled(TraceConfig(filter, ""), - TraceLog::RECORDING_MODE); - } - - void CancelTrace() { - WaitableEvent flush_complete_event( - WaitableEvent::ResetPolicy::AUTOMATIC, - WaitableEvent::InitialState::NOT_SIGNALED); - CancelTraceAsync(&flush_complete_event); - flush_complete_event.Wait(); - } - - void EndTraceAndFlush() { - num_flush_callbacks_ = 0; - WaitableEvent flush_complete_event( - WaitableEvent::ResetPolicy::AUTOMATIC, - WaitableEvent::InitialState::NOT_SIGNALED); - EndTraceAndFlushAsync(&flush_complete_event); - flush_complete_event.Wait(); - } - - // Used when testing thread-local buffers which requires the thread initiating - // flush to have a message loop. - void EndTraceAndFlushInThreadWithMessageLoop() { - WaitableEvent flush_complete_event( - WaitableEvent::ResetPolicy::AUTOMATIC, - WaitableEvent::InitialState::NOT_SIGNALED); - Thread flush_thread("flush"); - flush_thread.Start(); - flush_thread.task_runner()->PostTask( - FROM_HERE, base::Bind(&TraceEventTestFixture::EndTraceAndFlushAsync, - base::Unretained(this), &flush_complete_event)); - flush_complete_event.Wait(); - } - - void CancelTraceAsync(WaitableEvent* flush_complete_event) { - TraceLog::GetInstance()->CancelTracing( - base::Bind(&TraceEventTestFixture::OnTraceDataCollected, - base::Unretained(static_cast<TraceEventTestFixture*>(this)), - base::Unretained(flush_complete_event))); - } - - void EndTraceAndFlushAsync(WaitableEvent* flush_complete_event) { - TraceLog::GetInstance()->SetDisabled(TraceLog::RECORDING_MODE | - TraceLog::FILTERING_MODE); - TraceLog::GetInstance()->Flush( - base::Bind(&TraceEventTestFixture::OnTraceDataCollected, - base::Unretained(static_cast<TraceEventTestFixture*>(this)), - base::Unretained(flush_complete_event))); - } - - void SetUp() override { - const char* name = PlatformThread::GetName(); - old_thread_name_ = name ? strdup(name) : NULL; - - TraceLog::DeleteForTesting(); - TraceLog* tracelog = TraceLog::GetInstance(); - ASSERT_TRUE(tracelog); - ASSERT_FALSE(tracelog->IsEnabled()); - trace_buffer_.SetOutputCallback(json_output_.GetCallback()); - num_flush_callbacks_ = 0; - } - void TearDown() override { - if (TraceLog::GetInstance()) - EXPECT_FALSE(TraceLog::GetInstance()->IsEnabled()); - PlatformThread::SetName(old_thread_name_ ? old_thread_name_ : ""); - free(old_thread_name_); - old_thread_name_ = NULL; - // We want our singleton torn down after each test. - TraceLog::DeleteForTesting(); - } - - char* old_thread_name_; - ListValue trace_parsed_; - TraceResultBuffer trace_buffer_; - TraceResultBuffer::SimpleOutput json_output_; - size_t num_flush_callbacks_; - - private: - // We want our singleton torn down after each test. - ShadowingAtExitManager at_exit_manager_; - Lock lock_; -}; - -void TraceEventTestFixture::OnTraceDataCollected( - WaitableEvent* flush_complete_event, - const scoped_refptr<base::RefCountedString>& events_str, - bool has_more_events) { - num_flush_callbacks_++; - if (num_flush_callbacks_ > 1) { - EXPECT_FALSE(events_str->data().empty()); - } - AutoLock lock(lock_); - json_output_.json_output.clear(); - trace_buffer_.Start(); - trace_buffer_.AddFragment(events_str->data()); - trace_buffer_.Finish(); - - std::unique_ptr<Value> root = base::JSONReader::Read( - json_output_.json_output, JSON_PARSE_RFC | JSON_DETACHABLE_CHILDREN); - - if (!root.get()) { - LOG(ERROR) << json_output_.json_output; - } - - ListValue* root_list = NULL; - ASSERT_TRUE(root.get()); - ASSERT_TRUE(root->GetAsList(&root_list)); - - // Move items into our aggregate collection - while (root_list->GetSize()) { - std::unique_ptr<Value> item; - root_list->Remove(0, &item); - trace_parsed_.Append(std::move(item)); - } - - if (!has_more_events) - flush_complete_event->Signal(); -} - -static bool CompareJsonValues(const std::string& lhs, - const std::string& rhs, - CompareOp op) { - switch (op) { - case IS_EQUAL: - return lhs == rhs; - case IS_NOT_EQUAL: - return lhs != rhs; - default: - CHECK(0); - } - return false; -} - -static bool IsKeyValueInDict(const JsonKeyValue* key_value, - DictionaryValue* dict) { - Value* value = NULL; - std::string value_str; - if (dict->Get(key_value->key, &value) && - value->GetAsString(&value_str) && - CompareJsonValues(value_str, key_value->value, key_value->op)) - return true; - - // Recurse to test arguments - DictionaryValue* args_dict = NULL; - dict->GetDictionary("args", &args_dict); - if (args_dict) - return IsKeyValueInDict(key_value, args_dict); - - return false; -} - -static bool IsAllKeyValueInDict(const JsonKeyValue* key_values, - DictionaryValue* dict) { - // Scan all key_values, they must all be present and equal. - while (key_values && key_values->key) { - if (!IsKeyValueInDict(key_values, dict)) - return false; - ++key_values; - } - return true; -} - -DictionaryValue* TraceEventTestFixture::FindMatchingTraceEntry( - const JsonKeyValue* key_values) { - // Scan all items - size_t trace_parsed_count = trace_parsed_.GetSize(); - for (size_t i = 0; i < trace_parsed_count; i++) { - Value* value = NULL; - trace_parsed_.Get(i, &value); - if (!value || value->GetType() != Value::Type::DICTIONARY) - continue; - DictionaryValue* dict = static_cast<DictionaryValue*>(value); - - if (IsAllKeyValueInDict(key_values, dict)) - return dict; - } - return NULL; -} - -void TraceEventTestFixture::DropTracedMetadataRecords() { - std::unique_ptr<ListValue> old_trace_parsed(trace_parsed_.CreateDeepCopy()); - size_t old_trace_parsed_size = old_trace_parsed->GetSize(); - trace_parsed_.Clear(); - - for (size_t i = 0; i < old_trace_parsed_size; i++) { - Value* value = nullptr; - old_trace_parsed->Get(i, &value); - if (!value || value->GetType() != Value::Type::DICTIONARY) { - trace_parsed_.Append(value->CreateDeepCopy()); - continue; - } - DictionaryValue* dict = static_cast<DictionaryValue*>(value); - std::string tmp; - if (dict->GetString("ph", &tmp) && tmp == "M") - continue; - - trace_parsed_.Append(value->CreateDeepCopy()); - } -} - -DictionaryValue* TraceEventTestFixture::FindNamePhase(const char* name, - const char* phase) { - JsonKeyValue key_values[] = { - {"name", name, IS_EQUAL}, - {"ph", phase, IS_EQUAL}, - {0, 0, IS_EQUAL} - }; - return FindMatchingTraceEntry(key_values); -} - -DictionaryValue* TraceEventTestFixture::FindNamePhaseKeyValue( - const char* name, - const char* phase, - const char* key, - const char* value) { - JsonKeyValue key_values[] = { - {"name", name, IS_EQUAL}, - {"ph", phase, IS_EQUAL}, - {key, value, IS_EQUAL}, - {0, 0, IS_EQUAL} - }; - return FindMatchingTraceEntry(key_values); -} - -bool TraceEventTestFixture::FindMatchingValue(const char* key, - const char* value) { - JsonKeyValue key_values[] = { - {key, value, IS_EQUAL}, - {0, 0, IS_EQUAL} - }; - return FindMatchingTraceEntry(key_values); -} - -bool TraceEventTestFixture::FindNonMatchingValue(const char* key, - const char* value) { - JsonKeyValue key_values[] = { - {key, value, IS_NOT_EQUAL}, - {0, 0, IS_EQUAL} - }; - return FindMatchingTraceEntry(key_values); -} - -bool IsStringInDict(const char* string_to_match, const DictionaryValue* dict) { - for (DictionaryValue::Iterator it(*dict); !it.IsAtEnd(); it.Advance()) { - if (it.key().find(string_to_match) != std::string::npos) - return true; - - std::string value_str; - it.value().GetAsString(&value_str); - if (value_str.find(string_to_match) != std::string::npos) - return true; - } - - // Recurse to test arguments - const DictionaryValue* args_dict = NULL; - dict->GetDictionary("args", &args_dict); - if (args_dict) - return IsStringInDict(string_to_match, args_dict); - - return false; -} - -const DictionaryValue* FindTraceEntry( - const ListValue& trace_parsed, - const char* string_to_match, - const DictionaryValue* match_after_this_item = NULL) { - // Scan all items - size_t trace_parsed_count = trace_parsed.GetSize(); - for (size_t i = 0; i < trace_parsed_count; i++) { - const Value* value = NULL; - trace_parsed.Get(i, &value); - if (match_after_this_item) { - if (value == match_after_this_item) - match_after_this_item = NULL; - continue; - } - if (!value || value->GetType() != Value::Type::DICTIONARY) - continue; - const DictionaryValue* dict = static_cast<const DictionaryValue*>(value); - - if (IsStringInDict(string_to_match, dict)) - return dict; - } - return NULL; -} - -std::vector<const DictionaryValue*> FindTraceEntries( - const ListValue& trace_parsed, - const char* string_to_match) { - std::vector<const DictionaryValue*> hits; - size_t trace_parsed_count = trace_parsed.GetSize(); - for (size_t i = 0; i < trace_parsed_count; i++) { - const Value* value = NULL; - trace_parsed.Get(i, &value); - if (!value || value->GetType() != Value::Type::DICTIONARY) - continue; - const DictionaryValue* dict = static_cast<const DictionaryValue*>(value); - - if (IsStringInDict(string_to_match, dict)) - hits.push_back(dict); - } - return hits; -} - -const char kControlCharacters[] = "\001\002\003\n\r"; - -void TraceWithAllMacroVariants(WaitableEvent* task_complete_event) { - { - TRACE_EVENT0("all", "TRACE_EVENT0 call"); - TRACE_EVENT1("all", "TRACE_EVENT1 call", "name1", "value1"); - TRACE_EVENT2("all", "TRACE_EVENT2 call", - "name1", "\"value1\"", - "name2", "value\\2"); - - TRACE_EVENT_INSTANT0("all", "TRACE_EVENT_INSTANT0 call", - TRACE_EVENT_SCOPE_GLOBAL); - TRACE_EVENT_INSTANT1("all", "TRACE_EVENT_INSTANT1 call", - TRACE_EVENT_SCOPE_PROCESS, "name1", "value1"); - TRACE_EVENT_INSTANT2("all", "TRACE_EVENT_INSTANT2 call", - TRACE_EVENT_SCOPE_THREAD, - "name1", "value1", - "name2", "value2"); - - TRACE_EVENT_BEGIN0("all", "TRACE_EVENT_BEGIN0 call"); - TRACE_EVENT_BEGIN1("all", "TRACE_EVENT_BEGIN1 call", "name1", "value1"); - TRACE_EVENT_BEGIN2("all", "TRACE_EVENT_BEGIN2 call", - "name1", "value1", - "name2", "value2"); - - TRACE_EVENT_END0("all", "TRACE_EVENT_END0 call"); - TRACE_EVENT_END1("all", "TRACE_EVENT_END1 call", "name1", "value1"); - TRACE_EVENT_END2("all", "TRACE_EVENT_END2 call", - "name1", "value1", - "name2", "value2"); - - TRACE_EVENT_ASYNC_BEGIN0("all", "TRACE_EVENT_ASYNC_BEGIN0 call", kAsyncId); - TRACE_EVENT_ASYNC_BEGIN1("all", "TRACE_EVENT_ASYNC_BEGIN1 call", kAsyncId, - "name1", "value1"); - TRACE_EVENT_ASYNC_BEGIN2("all", "TRACE_EVENT_ASYNC_BEGIN2 call", kAsyncId, - "name1", "value1", - "name2", "value2"); - - TRACE_EVENT_ASYNC_STEP_INTO0("all", "TRACE_EVENT_ASYNC_STEP_INTO0 call", - kAsyncId, "step_begin1"); - TRACE_EVENT_ASYNC_STEP_INTO1("all", "TRACE_EVENT_ASYNC_STEP_INTO1 call", - kAsyncId, "step_begin2", "name1", "value1"); - - TRACE_EVENT_ASYNC_END0("all", "TRACE_EVENT_ASYNC_END0 call", kAsyncId); - TRACE_EVENT_ASYNC_END1("all", "TRACE_EVENT_ASYNC_END1 call", kAsyncId, - "name1", "value1"); - TRACE_EVENT_ASYNC_END2("all", "TRACE_EVENT_ASYNC_END2 call", kAsyncId, - "name1", "value1", - "name2", "value2"); - - TRACE_EVENT_FLOW_BEGIN0("all", "TRACE_EVENT_FLOW_BEGIN0 call", kFlowId); - TRACE_EVENT_FLOW_STEP0("all", "TRACE_EVENT_FLOW_STEP0 call", - kFlowId, "step1"); - TRACE_EVENT_FLOW_END_BIND_TO_ENCLOSING0("all", - "TRACE_EVENT_FLOW_END_BIND_TO_ENCLOSING0 call", kFlowId); - - TRACE_COUNTER1("all", "TRACE_COUNTER1 call", 31415); - TRACE_COUNTER2("all", "TRACE_COUNTER2 call", - "a", 30000, - "b", 1415); - - TRACE_COUNTER_WITH_TIMESTAMP1("all", "TRACE_COUNTER_WITH_TIMESTAMP1 call", - TimeTicks::FromInternalValue(42), 31415); - TRACE_COUNTER_WITH_TIMESTAMP2("all", "TRACE_COUNTER_WITH_TIMESTAMP2 call", - TimeTicks::FromInternalValue(42), - "a", 30000, "b", 1415); - - TRACE_COUNTER_ID1("all", "TRACE_COUNTER_ID1 call", 0x319009, 31415); - TRACE_COUNTER_ID2("all", "TRACE_COUNTER_ID2 call", 0x319009, - "a", 30000, "b", 1415); - - TRACE_EVENT_COPY_BEGIN_WITH_ID_TID_AND_TIMESTAMP0("all", - "TRACE_EVENT_COPY_BEGIN_WITH_ID_TID_AND_TIMESTAMP0 call", - kAsyncId, kThreadId, TimeTicks::FromInternalValue(12345)); - TRACE_EVENT_COPY_END_WITH_ID_TID_AND_TIMESTAMP0("all", - "TRACE_EVENT_COPY_END_WITH_ID_TID_AND_TIMESTAMP0 call", - kAsyncId, kThreadId, TimeTicks::FromInternalValue(23456)); - - TRACE_EVENT_BEGIN_WITH_ID_TID_AND_TIMESTAMP0("all", - "TRACE_EVENT_BEGIN_WITH_ID_TID_AND_TIMESTAMP0 call", - kAsyncId2, kThreadId, TimeTicks::FromInternalValue(34567)); - TRACE_EVENT_ASYNC_STEP_PAST0("all", "TRACE_EVENT_ASYNC_STEP_PAST0 call", - kAsyncId2, "step_end1"); - TRACE_EVENT_ASYNC_STEP_PAST1("all", "TRACE_EVENT_ASYNC_STEP_PAST1 call", - kAsyncId2, "step_end2", "name1", "value1"); - - TRACE_EVENT_END_WITH_ID_TID_AND_TIMESTAMP0("all", - "TRACE_EVENT_END_WITH_ID_TID_AND_TIMESTAMP0 call", - kAsyncId2, kThreadId, TimeTicks::FromInternalValue(45678)); - - TRACE_EVENT_OBJECT_CREATED_WITH_ID("all", "tracked object 1", 0x42); - TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID( - "all", "tracked object 1", 0x42, "hello"); - TRACE_EVENT_OBJECT_DELETED_WITH_ID("all", "tracked object 1", 0x42); - - TraceScopedTrackableObject<int> trackable("all", "tracked object 2", - 0x2128506); - trackable.snapshot("world"); - - TRACE_EVENT_OBJECT_CREATED_WITH_ID( - "all", "tracked object 3", TRACE_ID_WITH_SCOPE("scope", 0x42)); - TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID( - "all", "tracked object 3", TRACE_ID_WITH_SCOPE("scope", 0x42), "hello"); - TRACE_EVENT_OBJECT_DELETED_WITH_ID( - "all", "tracked object 3", TRACE_ID_WITH_SCOPE("scope", 0x42)); - - TRACE_EVENT1(kControlCharacters, kControlCharacters, - kControlCharacters, kControlCharacters); - - uint64_t context_id = 0x20151021; - - TRACE_EVENT_ENTER_CONTEXT("all", "TRACE_EVENT_ENTER_CONTEXT call", - TRACE_ID_WITH_SCOPE("scope", context_id)); - TRACE_EVENT_LEAVE_CONTEXT("all", "TRACE_EVENT_LEAVE_CONTEXT call", - TRACE_ID_WITH_SCOPE("scope", context_id)); - TRACE_EVENT_SCOPED_CONTEXT("disabled-by-default-cat", - "TRACE_EVENT_SCOPED_CONTEXT disabled call", - context_id); - TRACE_EVENT_SCOPED_CONTEXT("all", "TRACE_EVENT_SCOPED_CONTEXT call", - context_id); - - TRACE_LINK_IDS("all", "TRACE_LINK_IDS simple call", 0x1000, 0x2000); - TRACE_LINK_IDS("all", "TRACE_LINK_IDS scoped call", - TRACE_ID_WITH_SCOPE("scope 1", 0x1000), - TRACE_ID_WITH_SCOPE("scope 2", 0x2000)); - TRACE_LINK_IDS("all", "TRACE_LINK_IDS to a local ID", 0x1000, - TRACE_ID_LOCAL(0x2000)); - TRACE_LINK_IDS("all", "TRACE_LINK_IDS to a global ID", 0x1000, - TRACE_ID_GLOBAL(0x2000)); - TRACE_LINK_IDS("all", "TRACE_LINK_IDS to a composite ID", 0x1000, - TRACE_ID_WITH_SCOPE("scope 1", 0x2000, 0x3000)); - - TRACE_EVENT_ASYNC_BEGIN0("all", "async default process scope", 0x1000); - TRACE_EVENT_ASYNC_BEGIN0("all", "async local id", TRACE_ID_LOCAL(0x2000)); - TRACE_EVENT_ASYNC_BEGIN0("all", "async global id", TRACE_ID_GLOBAL(0x3000)); - TRACE_EVENT_ASYNC_BEGIN0("all", "async global id with scope string", - TRACE_ID_WITH_SCOPE("scope string", - TRACE_ID_GLOBAL(0x4000))); - } // Scope close causes TRACE_EVENT0 etc to send their END events. - - if (task_complete_event) - task_complete_event->Signal(); -} - -void ValidateAllTraceMacrosCreatedData(const ListValue& trace_parsed) { - const DictionaryValue* item = NULL; - -#define EXPECT_FIND_(string) \ - item = FindTraceEntry(trace_parsed, string); \ - EXPECT_TRUE(item); -#define EXPECT_NOT_FIND_(string) \ - item = FindTraceEntry(trace_parsed, string); \ - EXPECT_FALSE(item); -#define EXPECT_SUB_FIND_(string) \ - if (item) \ - EXPECT_TRUE(IsStringInDict(string, item)); - - EXPECT_FIND_("TRACE_EVENT0 call"); - { - std::string ph; - std::string ph_end; - EXPECT_TRUE((item = FindTraceEntry(trace_parsed, "TRACE_EVENT0 call"))); - EXPECT_TRUE((item && item->GetString("ph", &ph))); - EXPECT_EQ("X", ph); - item = FindTraceEntry(trace_parsed, "TRACE_EVENT0 call", item); - EXPECT_FALSE(item); - } - EXPECT_FIND_("TRACE_EVENT1 call"); - EXPECT_SUB_FIND_("name1"); - EXPECT_SUB_FIND_("value1"); - EXPECT_FIND_("TRACE_EVENT2 call"); - EXPECT_SUB_FIND_("name1"); - EXPECT_SUB_FIND_("\"value1\""); - EXPECT_SUB_FIND_("name2"); - EXPECT_SUB_FIND_("value\\2"); - - EXPECT_FIND_("TRACE_EVENT_INSTANT0 call"); - { - std::string scope; - EXPECT_TRUE((item && item->GetString("s", &scope))); - EXPECT_EQ("g", scope); - } - EXPECT_FIND_("TRACE_EVENT_INSTANT1 call"); - { - std::string scope; - EXPECT_TRUE((item && item->GetString("s", &scope))); - EXPECT_EQ("p", scope); - } - EXPECT_SUB_FIND_("name1"); - EXPECT_SUB_FIND_("value1"); - EXPECT_FIND_("TRACE_EVENT_INSTANT2 call"); - { - std::string scope; - EXPECT_TRUE((item && item->GetString("s", &scope))); - EXPECT_EQ("t", scope); - } - EXPECT_SUB_FIND_("name1"); - EXPECT_SUB_FIND_("value1"); - EXPECT_SUB_FIND_("name2"); - EXPECT_SUB_FIND_("value2"); - - EXPECT_FIND_("TRACE_EVENT_BEGIN0 call"); - EXPECT_FIND_("TRACE_EVENT_BEGIN1 call"); - EXPECT_SUB_FIND_("name1"); - EXPECT_SUB_FIND_("value1"); - EXPECT_FIND_("TRACE_EVENT_BEGIN2 call"); - EXPECT_SUB_FIND_("name1"); - EXPECT_SUB_FIND_("value1"); - EXPECT_SUB_FIND_("name2"); - EXPECT_SUB_FIND_("value2"); - - EXPECT_FIND_("TRACE_EVENT_END0 call"); - EXPECT_FIND_("TRACE_EVENT_END1 call"); - EXPECT_SUB_FIND_("name1"); - EXPECT_SUB_FIND_("value1"); - EXPECT_FIND_("TRACE_EVENT_END2 call"); - EXPECT_SUB_FIND_("name1"); - EXPECT_SUB_FIND_("value1"); - EXPECT_SUB_FIND_("name2"); - EXPECT_SUB_FIND_("value2"); - - EXPECT_FIND_("TRACE_EVENT_ASYNC_BEGIN0 call"); - EXPECT_SUB_FIND_("id"); - EXPECT_SUB_FIND_(kAsyncIdStr); - EXPECT_FIND_("TRACE_EVENT_ASYNC_BEGIN1 call"); - EXPECT_SUB_FIND_("id"); - EXPECT_SUB_FIND_(kAsyncIdStr); - EXPECT_SUB_FIND_("name1"); - EXPECT_SUB_FIND_("value1"); - EXPECT_FIND_("TRACE_EVENT_ASYNC_BEGIN2 call"); - EXPECT_SUB_FIND_("id"); - EXPECT_SUB_FIND_(kAsyncIdStr); - EXPECT_SUB_FIND_("name1"); - EXPECT_SUB_FIND_("value1"); - EXPECT_SUB_FIND_("name2"); - EXPECT_SUB_FIND_("value2"); - - EXPECT_FIND_("TRACE_EVENT_ASYNC_STEP_INTO0 call"); - EXPECT_SUB_FIND_("id"); - EXPECT_SUB_FIND_(kAsyncIdStr); - EXPECT_SUB_FIND_("step_begin1"); - EXPECT_FIND_("TRACE_EVENT_ASYNC_STEP_INTO1 call"); - EXPECT_SUB_FIND_("id"); - EXPECT_SUB_FIND_(kAsyncIdStr); - EXPECT_SUB_FIND_("step_begin2"); - EXPECT_SUB_FIND_("name1"); - EXPECT_SUB_FIND_("value1"); - - EXPECT_FIND_("TRACE_EVENT_ASYNC_END0 call"); - EXPECT_SUB_FIND_("id"); - EXPECT_SUB_FIND_(kAsyncIdStr); - EXPECT_FIND_("TRACE_EVENT_ASYNC_END1 call"); - EXPECT_SUB_FIND_("id"); - EXPECT_SUB_FIND_(kAsyncIdStr); - EXPECT_SUB_FIND_("name1"); - EXPECT_SUB_FIND_("value1"); - EXPECT_FIND_("TRACE_EVENT_ASYNC_END2 call"); - EXPECT_SUB_FIND_("id"); - EXPECT_SUB_FIND_(kAsyncIdStr); - EXPECT_SUB_FIND_("name1"); - EXPECT_SUB_FIND_("value1"); - EXPECT_SUB_FIND_("name2"); - EXPECT_SUB_FIND_("value2"); - - EXPECT_FIND_("TRACE_EVENT_FLOW_BEGIN0 call"); - EXPECT_SUB_FIND_("id"); - EXPECT_SUB_FIND_(kFlowIdStr); - EXPECT_FIND_("TRACE_EVENT_FLOW_STEP0 call"); - EXPECT_SUB_FIND_("id"); - EXPECT_SUB_FIND_(kFlowIdStr); - EXPECT_SUB_FIND_("step1"); - EXPECT_FIND_("TRACE_EVENT_FLOW_END_BIND_TO_ENCLOSING0 call"); - EXPECT_SUB_FIND_("id"); - EXPECT_SUB_FIND_(kFlowIdStr); - - EXPECT_FIND_("TRACE_COUNTER1 call"); - { - std::string ph; - EXPECT_TRUE((item && item->GetString("ph", &ph))); - EXPECT_EQ("C", ph); - - int value; - EXPECT_TRUE((item && item->GetInteger("args.value", &value))); - EXPECT_EQ(31415, value); - } - - EXPECT_FIND_("TRACE_COUNTER2 call"); - { - std::string ph; - EXPECT_TRUE((item && item->GetString("ph", &ph))); - EXPECT_EQ("C", ph); - - int value; - EXPECT_TRUE((item && item->GetInteger("args.a", &value))); - EXPECT_EQ(30000, value); - - EXPECT_TRUE((item && item->GetInteger("args.b", &value))); - EXPECT_EQ(1415, value); - } - - EXPECT_FIND_("TRACE_COUNTER_WITH_TIMESTAMP1 call"); - { - std::string ph; - EXPECT_TRUE((item && item->GetString("ph", &ph))); - EXPECT_EQ("C", ph); - - int value; - EXPECT_TRUE((item && item->GetInteger("args.value", &value))); - EXPECT_EQ(31415, value); - - int ts; - EXPECT_TRUE((item && item->GetInteger("ts", &ts))); - EXPECT_EQ(42, ts); - } - - EXPECT_FIND_("TRACE_COUNTER_WITH_TIMESTAMP2 call"); - { - std::string ph; - EXPECT_TRUE((item && item->GetString("ph", &ph))); - EXPECT_EQ("C", ph); - - int value; - EXPECT_TRUE((item && item->GetInteger("args.a", &value))); - EXPECT_EQ(30000, value); - - EXPECT_TRUE((item && item->GetInteger("args.b", &value))); - EXPECT_EQ(1415, value); - - int ts; - EXPECT_TRUE((item && item->GetInteger("ts", &ts))); - EXPECT_EQ(42, ts); - } - - EXPECT_FIND_("TRACE_COUNTER_ID1 call"); - { - std::string id; - EXPECT_TRUE((item && item->GetString("id", &id))); - EXPECT_EQ("0x319009", id); - - std::string ph; - EXPECT_TRUE((item && item->GetString("ph", &ph))); - EXPECT_EQ("C", ph); - - int value; - EXPECT_TRUE((item && item->GetInteger("args.value", &value))); - EXPECT_EQ(31415, value); - } - - EXPECT_FIND_("TRACE_COUNTER_ID2 call"); - { - std::string id; - EXPECT_TRUE((item && item->GetString("id", &id))); - EXPECT_EQ("0x319009", id); - - std::string ph; - EXPECT_TRUE((item && item->GetString("ph", &ph))); - EXPECT_EQ("C", ph); - - int value; - EXPECT_TRUE((item && item->GetInteger("args.a", &value))); - EXPECT_EQ(30000, value); - - EXPECT_TRUE((item && item->GetInteger("args.b", &value))); - EXPECT_EQ(1415, value); - } - - EXPECT_FIND_("TRACE_EVENT_COPY_BEGIN_WITH_ID_TID_AND_TIMESTAMP0 call"); - { - int val; - EXPECT_TRUE((item && item->GetInteger("ts", &val))); - EXPECT_EQ(12345, val); - EXPECT_TRUE((item && item->GetInteger("tid", &val))); - EXPECT_EQ(kThreadId, val); - std::string id; - EXPECT_TRUE((item && item->GetString("id", &id))); - EXPECT_EQ(kAsyncIdStr, id); - } - - EXPECT_FIND_("TRACE_EVENT_COPY_END_WITH_ID_TID_AND_TIMESTAMP0 call"); - { - int val; - EXPECT_TRUE((item && item->GetInteger("ts", &val))); - EXPECT_EQ(23456, val); - EXPECT_TRUE((item && item->GetInteger("tid", &val))); - EXPECT_EQ(kThreadId, val); - std::string id; - EXPECT_TRUE((item && item->GetString("id", &id))); - EXPECT_EQ(kAsyncIdStr, id); - } - - EXPECT_FIND_("TRACE_EVENT_BEGIN_WITH_ID_TID_AND_TIMESTAMP0 call"); - { - int val; - EXPECT_TRUE((item && item->GetInteger("ts", &val))); - EXPECT_EQ(34567, val); - EXPECT_TRUE((item && item->GetInteger("tid", &val))); - EXPECT_EQ(kThreadId, val); - std::string id; - EXPECT_TRUE((item && item->GetString("id", &id))); - EXPECT_EQ(kAsyncId2Str, id); - } - - EXPECT_FIND_("TRACE_EVENT_ASYNC_STEP_PAST0 call"); - { - EXPECT_SUB_FIND_("id"); - EXPECT_SUB_FIND_(kAsyncId2Str); - EXPECT_SUB_FIND_("step_end1"); - EXPECT_FIND_("TRACE_EVENT_ASYNC_STEP_PAST1 call"); - EXPECT_SUB_FIND_("id"); - EXPECT_SUB_FIND_(kAsyncId2Str); - EXPECT_SUB_FIND_("step_end2"); - EXPECT_SUB_FIND_("name1"); - EXPECT_SUB_FIND_("value1"); - } - - EXPECT_FIND_("TRACE_EVENT_END_WITH_ID_TID_AND_TIMESTAMP0 call"); - { - int val; - EXPECT_TRUE((item && item->GetInteger("ts", &val))); - EXPECT_EQ(45678, val); - EXPECT_TRUE((item && item->GetInteger("tid", &val))); - EXPECT_EQ(kThreadId, val); - std::string id; - EXPECT_TRUE((item && item->GetString("id", &id))); - EXPECT_EQ(kAsyncId2Str, id); - } - - EXPECT_FIND_("tracked object 1"); - { - std::string phase; - std::string id; - std::string snapshot; - - EXPECT_TRUE((item && item->GetString("ph", &phase))); - EXPECT_EQ("N", phase); - EXPECT_FALSE((item && item->HasKey("scope"))); - EXPECT_TRUE((item && item->GetString("id", &id))); - EXPECT_EQ("0x42", id); - - item = FindTraceEntry(trace_parsed, "tracked object 1", item); - EXPECT_TRUE(item); - EXPECT_TRUE(item && item->GetString("ph", &phase)); - EXPECT_EQ("O", phase); - EXPECT_FALSE((item && item->HasKey("scope"))); - EXPECT_TRUE(item && item->GetString("id", &id)); - EXPECT_EQ("0x42", id); - EXPECT_TRUE(item && item->GetString("args.snapshot", &snapshot)); - EXPECT_EQ("hello", snapshot); - - item = FindTraceEntry(trace_parsed, "tracked object 1", item); - EXPECT_TRUE(item); - EXPECT_TRUE(item && item->GetString("ph", &phase)); - EXPECT_EQ("D", phase); - EXPECT_FALSE((item && item->HasKey("scope"))); - EXPECT_TRUE(item && item->GetString("id", &id)); - EXPECT_EQ("0x42", id); - } - - EXPECT_FIND_("tracked object 2"); - { - std::string phase; - std::string id; - std::string snapshot; - - EXPECT_TRUE(item && item->GetString("ph", &phase)); - EXPECT_EQ("N", phase); - EXPECT_TRUE(item && item->GetString("id", &id)); - EXPECT_EQ("0x2128506", id); - - item = FindTraceEntry(trace_parsed, "tracked object 2", item); - EXPECT_TRUE(item); - EXPECT_TRUE(item && item->GetString("ph", &phase)); - EXPECT_EQ("O", phase); - EXPECT_TRUE(item && item->GetString("id", &id)); - EXPECT_EQ("0x2128506", id); - EXPECT_TRUE(item && item->GetString("args.snapshot", &snapshot)); - EXPECT_EQ("world", snapshot); - - item = FindTraceEntry(trace_parsed, "tracked object 2", item); - EXPECT_TRUE(item); - EXPECT_TRUE(item && item->GetString("ph", &phase)); - EXPECT_EQ("D", phase); - EXPECT_TRUE(item && item->GetString("id", &id)); - EXPECT_EQ("0x2128506", id); - } - - EXPECT_FIND_("tracked object 3"); - { - std::string phase; - std::string scope; - std::string id; - std::string snapshot; - - EXPECT_TRUE((item && item->GetString("ph", &phase))); - EXPECT_EQ("N", phase); - EXPECT_TRUE((item && item->GetString("scope", &scope))); - EXPECT_EQ("scope", scope); - EXPECT_TRUE((item && item->GetString("id", &id))); - EXPECT_EQ("0x42", id); - - item = FindTraceEntry(trace_parsed, "tracked object 3", item); - EXPECT_TRUE(item); - EXPECT_TRUE(item && item->GetString("ph", &phase)); - EXPECT_EQ("O", phase); - EXPECT_TRUE((item && item->GetString("scope", &scope))); - EXPECT_EQ("scope", scope); - EXPECT_TRUE(item && item->GetString("id", &id)); - EXPECT_EQ("0x42", id); - EXPECT_TRUE(item && item->GetString("args.snapshot", &snapshot)); - EXPECT_EQ("hello", snapshot); - - item = FindTraceEntry(trace_parsed, "tracked object 3", item); - EXPECT_TRUE(item); - EXPECT_TRUE(item && item->GetString("ph", &phase)); - EXPECT_EQ("D", phase); - EXPECT_TRUE((item && item->GetString("scope", &scope))); - EXPECT_EQ("scope", scope); - EXPECT_TRUE(item && item->GetString("id", &id)); - EXPECT_EQ("0x42", id); - } - - EXPECT_FIND_(kControlCharacters); - EXPECT_SUB_FIND_(kControlCharacters); - - EXPECT_FIND_("TRACE_EVENT_ENTER_CONTEXT call"); - { - std::string ph; - EXPECT_TRUE((item && item->GetString("ph", &ph))); - EXPECT_EQ("(", ph); - - std::string scope; - std::string id; - EXPECT_TRUE((item && item->GetString("scope", &scope))); - EXPECT_EQ("scope", scope); - EXPECT_TRUE((item && item->GetString("id", &id))); - EXPECT_EQ("0x20151021", id); - } - - EXPECT_FIND_("TRACE_EVENT_LEAVE_CONTEXT call"); - { - std::string ph; - EXPECT_TRUE((item && item->GetString("ph", &ph))); - EXPECT_EQ(")", ph); - - std::string scope; - std::string id; - EXPECT_TRUE((item && item->GetString("scope", &scope))); - EXPECT_EQ("scope", scope); - EXPECT_TRUE((item && item->GetString("id", &id))); - EXPECT_EQ("0x20151021", id); - } - - std::vector<const DictionaryValue*> scoped_context_calls = - FindTraceEntries(trace_parsed, "TRACE_EVENT_SCOPED_CONTEXT call"); - EXPECT_EQ(2u, scoped_context_calls.size()); - { - item = scoped_context_calls[0]; - std::string ph; - EXPECT_TRUE((item && item->GetString("ph", &ph))); - EXPECT_EQ("(", ph); - - std::string id; - EXPECT_FALSE((item && item->HasKey("scope"))); - EXPECT_TRUE((item && item->GetString("id", &id))); - EXPECT_EQ("0x20151021", id); - } - - { - item = scoped_context_calls[1]; - std::string ph; - EXPECT_TRUE((item && item->GetString("ph", &ph))); - EXPECT_EQ(")", ph); - - std::string id; - EXPECT_FALSE((item && item->HasKey("scope"))); - EXPECT_TRUE((item && item->GetString("id", &id))); - EXPECT_EQ("0x20151021", id); - } - - EXPECT_FIND_("TRACE_LINK_IDS simple call"); - { - std::string ph; - EXPECT_TRUE((item && item->GetString("ph", &ph))); - EXPECT_EQ("=", ph); - - EXPECT_FALSE((item && item->HasKey("scope"))); - std::string id1; - EXPECT_TRUE((item && item->GetString("id", &id1))); - EXPECT_EQ("0x1000", id1); - - EXPECT_FALSE((item && item->HasKey("args.linked_id.scope"))); - std::string id2; - EXPECT_TRUE((item && item->GetString("args.linked_id.id", &id2))); - EXPECT_EQ("0x2000", id2); - } - - EXPECT_FIND_("TRACE_LINK_IDS scoped call"); - { - std::string ph; - EXPECT_TRUE((item && item->GetString("ph", &ph))); - EXPECT_EQ("=", ph); - - std::string scope1; - EXPECT_TRUE((item && item->GetString("scope", &scope1))); - EXPECT_EQ("scope 1", scope1); - std::string id1; - EXPECT_TRUE((item && item->GetString("id", &id1))); - EXPECT_EQ("0x1000", id1); - - std::string scope2; - EXPECT_TRUE((item && item->GetString("args.linked_id.scope", &scope2))); - EXPECT_EQ("scope 2", scope2); - std::string id2; - EXPECT_TRUE((item && item->GetString("args.linked_id.id", &id2))); - EXPECT_EQ("0x2000", id2); - } - - EXPECT_FIND_("TRACE_LINK_IDS to a local ID"); - { - std::string ph; - EXPECT_TRUE((item && item->GetString("ph", &ph))); - EXPECT_EQ("=", ph); - - EXPECT_FALSE((item && item->HasKey("scope"))); - std::string id1; - EXPECT_TRUE((item && item->GetString("id", &id1))); - EXPECT_EQ("0x1000", id1); - - EXPECT_FALSE((item && item->HasKey("args.linked_id.scope"))); - std::string id2; - EXPECT_TRUE((item && item->GetString("args.linked_id.id2.local", &id2))); - EXPECT_EQ("0x2000", id2); - } - - EXPECT_FIND_("TRACE_LINK_IDS to a global ID"); - { - std::string ph; - EXPECT_TRUE((item && item->GetString("ph", &ph))); - EXPECT_EQ("=", ph); - - EXPECT_FALSE((item && item->HasKey("scope"))); - std::string id1; - EXPECT_TRUE((item && item->GetString("id", &id1))); - EXPECT_EQ("0x1000", id1); - - EXPECT_FALSE((item && item->HasKey("args.linked_id.scope"))); - std::string id2; - EXPECT_TRUE((item && item->GetString("args.linked_id.id2.global", &id2))); - EXPECT_EQ("0x2000", id2); - } - - EXPECT_FIND_("TRACE_LINK_IDS to a composite ID"); - { - std::string ph; - EXPECT_TRUE((item && item->GetString("ph", &ph))); - EXPECT_EQ("=", ph); - - EXPECT_FALSE(item->HasKey("scope")); - std::string id1; - EXPECT_TRUE(item->GetString("id", &id1)); - EXPECT_EQ("0x1000", id1); - - std::string scope; - EXPECT_TRUE(item->GetString("args.linked_id.scope", &scope)); - EXPECT_EQ("scope 1", scope); - std::string id2; - EXPECT_TRUE(item->GetString("args.linked_id.id", &id2)); - EXPECT_EQ(id2, "0x2000/0x3000"); - } - - EXPECT_FIND_("async default process scope"); - { - std::string ph; - EXPECT_TRUE((item && item->GetString("ph", &ph))); - EXPECT_EQ("S", ph); - - std::string id; - EXPECT_TRUE((item && item->GetString("id", &id))); - EXPECT_EQ("0x1000", id); - } - - EXPECT_FIND_("async local id"); - { - std::string ph; - EXPECT_TRUE((item && item->GetString("ph", &ph))); - EXPECT_EQ("S", ph); - - std::string id; - EXPECT_TRUE((item && item->GetString("id2.local", &id))); - EXPECT_EQ("0x2000", id); - } - - EXPECT_FIND_("async global id"); - { - std::string ph; - EXPECT_TRUE((item && item->GetString("ph", &ph))); - EXPECT_EQ("S", ph); - - std::string id; - EXPECT_TRUE((item && item->GetString("id2.global", &id))); - EXPECT_EQ("0x3000", id); - } - - EXPECT_FIND_("async global id with scope string"); - { - std::string ph; - EXPECT_TRUE((item && item->GetString("ph", &ph))); - EXPECT_EQ("S", ph); - - std::string id; - EXPECT_TRUE((item && item->GetString("id2.global", &id))); - EXPECT_EQ("0x4000", id); - std::string scope; - EXPECT_TRUE((item && item->GetString("scope", &scope))); - EXPECT_EQ("scope string", scope); - } -} - -void TraceManyInstantEvents(int thread_id, int num_events, - WaitableEvent* task_complete_event) { - for (int i = 0; i < num_events; i++) { - TRACE_EVENT_INSTANT2("all", "multi thread event", - TRACE_EVENT_SCOPE_THREAD, - "thread", thread_id, - "event", i); - } - - if (task_complete_event) - task_complete_event->Signal(); -} - -void ValidateInstantEventPresentOnEveryThread(const ListValue& trace_parsed, - int num_threads, - int num_events) { - std::map<int, std::map<int, bool> > results; - - size_t trace_parsed_count = trace_parsed.GetSize(); - for (size_t i = 0; i < trace_parsed_count; i++) { - const Value* value = NULL; - trace_parsed.Get(i, &value); - if (!value || value->GetType() != Value::Type::DICTIONARY) - continue; - const DictionaryValue* dict = static_cast<const DictionaryValue*>(value); - std::string name; - dict->GetString("name", &name); - if (name != "multi thread event") - continue; - - int thread = 0; - int event = 0; - EXPECT_TRUE(dict->GetInteger("args.thread", &thread)); - EXPECT_TRUE(dict->GetInteger("args.event", &event)); - results[thread][event] = true; - } - - EXPECT_FALSE(results[-1][-1]); - for (int thread = 0; thread < num_threads; thread++) { - for (int event = 0; event < num_events; event++) { - EXPECT_TRUE(results[thread][event]); - } - } -} - -void CheckTraceDefaultCategoryFilters(const TraceLog& trace_log) { - // Default enables all category filters except the disabled-by-default-* ones. - EXPECT_TRUE(*trace_log.GetCategoryGroupEnabled("foo")); - EXPECT_TRUE(*trace_log.GetCategoryGroupEnabled("bar")); - EXPECT_TRUE(*trace_log.GetCategoryGroupEnabled("foo,bar")); - EXPECT_TRUE(*trace_log.GetCategoryGroupEnabled( - "foo,disabled-by-default-foo")); - EXPECT_FALSE(*trace_log.GetCategoryGroupEnabled( - "disabled-by-default-foo,disabled-by-default-bar")); -} - -} // namespace - -// Simple Test for emitting data and validating it was received. -TEST_F(TraceEventTestFixture, DataCaptured) { - TraceLog::GetInstance()->SetEnabled(TraceConfig(kRecordAllCategoryFilter, ""), - TraceLog::RECORDING_MODE); - - TraceWithAllMacroVariants(NULL); - - EndTraceAndFlush(); - - ValidateAllTraceMacrosCreatedData(trace_parsed_); -} - -// Emit some events and validate that only empty strings are received -// if we tell Flush() to discard events. -TEST_F(TraceEventTestFixture, DataDiscarded) { - TraceLog::GetInstance()->SetEnabled(TraceConfig(kRecordAllCategoryFilter, ""), - TraceLog::RECORDING_MODE); - - TraceWithAllMacroVariants(NULL); - - CancelTrace(); - - EXPECT_TRUE(trace_parsed_.empty()); -} - -class MockEnabledStateChangedObserver : - public TraceLog::EnabledStateObserver { - public: - MOCK_METHOD0(OnTraceLogEnabled, void()); - MOCK_METHOD0(OnTraceLogDisabled, void()); -}; - -TEST_F(TraceEventTestFixture, EnabledObserverFiresOnEnable) { - MockEnabledStateChangedObserver observer; - TraceLog::GetInstance()->AddEnabledStateObserver(&observer); - - EXPECT_CALL(observer, OnTraceLogEnabled()) - .Times(1); - TraceLog::GetInstance()->SetEnabled(TraceConfig(kRecordAllCategoryFilter, ""), - TraceLog::RECORDING_MODE); - testing::Mock::VerifyAndClear(&observer); - EXPECT_TRUE(TraceLog::GetInstance()->IsEnabled()); - - // Cleanup. - TraceLog::GetInstance()->RemoveEnabledStateObserver(&observer); - TraceLog::GetInstance()->SetDisabled(); -} - -TEST_F(TraceEventTestFixture, EnabledObserverDoesntFireOnSecondEnable) { - TraceLog::GetInstance()->SetEnabled(TraceConfig(kRecordAllCategoryFilter, ""), - TraceLog::RECORDING_MODE); - - testing::StrictMock<MockEnabledStateChangedObserver> observer; - TraceLog::GetInstance()->AddEnabledStateObserver(&observer); - - EXPECT_CALL(observer, OnTraceLogEnabled()) - .Times(0); - EXPECT_CALL(observer, OnTraceLogDisabled()) - .Times(0); - TraceLog::GetInstance()->SetEnabled(TraceConfig(kRecordAllCategoryFilter, ""), - TraceLog::RECORDING_MODE); - testing::Mock::VerifyAndClear(&observer); - EXPECT_TRUE(TraceLog::GetInstance()->IsEnabled()); - - // Cleanup. - TraceLog::GetInstance()->RemoveEnabledStateObserver(&observer); - TraceLog::GetInstance()->SetDisabled(); - TraceLog::GetInstance()->SetDisabled(); -} - -TEST_F(TraceEventTestFixture, EnabledObserverFiresOnFirstDisable) { - TraceConfig tc_inc_all("*", ""); - TraceLog::GetInstance()->SetEnabled(tc_inc_all, TraceLog::RECORDING_MODE); - TraceLog::GetInstance()->SetEnabled(tc_inc_all, TraceLog::RECORDING_MODE); - - testing::StrictMock<MockEnabledStateChangedObserver> observer; - TraceLog::GetInstance()->AddEnabledStateObserver(&observer); - - EXPECT_CALL(observer, OnTraceLogEnabled()) - .Times(0); - EXPECT_CALL(observer, OnTraceLogDisabled()) - .Times(1); - TraceLog::GetInstance()->SetDisabled(); - testing::Mock::VerifyAndClear(&observer); - - // Cleanup. - TraceLog::GetInstance()->RemoveEnabledStateObserver(&observer); - TraceLog::GetInstance()->SetDisabled(); -} - -TEST_F(TraceEventTestFixture, EnabledObserverFiresOnDisable) { - TraceLog::GetInstance()->SetEnabled(TraceConfig(kRecordAllCategoryFilter, ""), - TraceLog::RECORDING_MODE); - - MockEnabledStateChangedObserver observer; - TraceLog::GetInstance()->AddEnabledStateObserver(&observer); - - EXPECT_CALL(observer, OnTraceLogDisabled()) - .Times(1); - TraceLog::GetInstance()->SetDisabled(); - testing::Mock::VerifyAndClear(&observer); - - // Cleanup. - TraceLog::GetInstance()->RemoveEnabledStateObserver(&observer); -} - -// Tests the IsEnabled() state of TraceLog changes before callbacks. -class AfterStateChangeEnabledStateObserver - : public TraceLog::EnabledStateObserver { - public: - AfterStateChangeEnabledStateObserver() {} - ~AfterStateChangeEnabledStateObserver() override {} - - // TraceLog::EnabledStateObserver overrides: - void OnTraceLogEnabled() override { - EXPECT_TRUE(TraceLog::GetInstance()->IsEnabled()); - } - - void OnTraceLogDisabled() override { - EXPECT_FALSE(TraceLog::GetInstance()->IsEnabled()); - } -}; - -TEST_F(TraceEventTestFixture, ObserversFireAfterStateChange) { - AfterStateChangeEnabledStateObserver observer; - TraceLog::GetInstance()->AddEnabledStateObserver(&observer); - - TraceLog::GetInstance()->SetEnabled(TraceConfig(kRecordAllCategoryFilter, ""), - TraceLog::RECORDING_MODE); - EXPECT_TRUE(TraceLog::GetInstance()->IsEnabled()); - - TraceLog::GetInstance()->SetDisabled(); - EXPECT_FALSE(TraceLog::GetInstance()->IsEnabled()); - - TraceLog::GetInstance()->RemoveEnabledStateObserver(&observer); -} - -// Tests that a state observer can remove itself during a callback. -class SelfRemovingEnabledStateObserver - : public TraceLog::EnabledStateObserver { - public: - SelfRemovingEnabledStateObserver() {} - ~SelfRemovingEnabledStateObserver() override {} - - // TraceLog::EnabledStateObserver overrides: - void OnTraceLogEnabled() override {} - - void OnTraceLogDisabled() override { - TraceLog::GetInstance()->RemoveEnabledStateObserver(this); - } -}; - -TEST_F(TraceEventTestFixture, SelfRemovingObserver) { - ASSERT_EQ(0u, TraceLog::GetInstance()->GetObserverCountForTest()); - - SelfRemovingEnabledStateObserver observer; - TraceLog::GetInstance()->AddEnabledStateObserver(&observer); - EXPECT_EQ(1u, TraceLog::GetInstance()->GetObserverCountForTest()); - - TraceLog::GetInstance()->SetEnabled(TraceConfig(kRecordAllCategoryFilter, ""), - TraceLog::RECORDING_MODE); - TraceLog::GetInstance()->SetDisabled(); - // The observer removed itself on disable. - EXPECT_EQ(0u, TraceLog::GetInstance()->GetObserverCountForTest()); -} - -bool IsNewTrace() { - bool is_new_trace; - TRACE_EVENT_IS_NEW_TRACE(&is_new_trace); - return is_new_trace; -} - -TEST_F(TraceEventTestFixture, NewTraceRecording) { - ASSERT_FALSE(IsNewTrace()); - TraceLog::GetInstance()->SetEnabled(TraceConfig(kRecordAllCategoryFilter, ""), - TraceLog::RECORDING_MODE); - // First call to IsNewTrace() should succeed. But, the second shouldn't. - ASSERT_TRUE(IsNewTrace()); - ASSERT_FALSE(IsNewTrace()); - EndTraceAndFlush(); - - // IsNewTrace() should definitely be false now. - ASSERT_FALSE(IsNewTrace()); - - // Start another trace. IsNewTrace() should become true again, briefly, as - // before. - TraceLog::GetInstance()->SetEnabled(TraceConfig(kRecordAllCategoryFilter, ""), - TraceLog::RECORDING_MODE); - ASSERT_TRUE(IsNewTrace()); - ASSERT_FALSE(IsNewTrace()); - - // Cleanup. - EndTraceAndFlush(); -} - -TEST_F(TraceEventTestFixture, TestTraceFlush) { - size_t min_traces = 1; - size_t max_traces = 1; - do { - max_traces *= 2; - TraceLog::GetInstance()->SetEnabled(TraceConfig(), - TraceLog::RECORDING_MODE); - for (size_t i = 0; i < max_traces; i++) { - TRACE_EVENT_INSTANT0("x", "y", TRACE_EVENT_SCOPE_THREAD); - } - EndTraceAndFlush(); - } while (num_flush_callbacks_ < 2); - - while (min_traces + 50 < max_traces) { - size_t traces = (min_traces + max_traces) / 2; - TraceLog::GetInstance()->SetEnabled(TraceConfig(), - TraceLog::RECORDING_MODE); - for (size_t i = 0; i < traces; i++) { - TRACE_EVENT_INSTANT0("x", "y", TRACE_EVENT_SCOPE_THREAD); - } - EndTraceAndFlush(); - if (num_flush_callbacks_ < 2) { - min_traces = traces - 10; - } else { - max_traces = traces + 10; - } - } - - for (size_t traces = min_traces; traces < max_traces; traces++) { - TraceLog::GetInstance()->SetEnabled(TraceConfig(), - TraceLog::RECORDING_MODE); - for (size_t i = 0; i < traces; i++) { - TRACE_EVENT_INSTANT0("x", "y", TRACE_EVENT_SCOPE_THREAD); - } - EndTraceAndFlush(); - } -} - -TEST_F(TraceEventTestFixture, AddMetadataEvent) { - int num_calls = 0; - - class Convertable : public ConvertableToTraceFormat { - public: - explicit Convertable(int* num_calls) : num_calls_(num_calls) {} - ~Convertable() override {} - void AppendAsTraceFormat(std::string* out) const override { - (*num_calls_)++; - out->append("\"metadata_value\""); - } - - private: - int* num_calls_; - }; - - std::unique_ptr<ConvertableToTraceFormat> conv1(new Convertable(&num_calls)); - std::unique_ptr<Convertable> conv2(new Convertable(&num_calls)); - - BeginTrace(); - TRACE_EVENT_API_ADD_METADATA_EVENT( - TraceLog::GetCategoryGroupEnabled("__metadata"), "metadata_event_1", - "metadata_arg_name", std::move(conv1)); - TRACE_EVENT_API_ADD_METADATA_EVENT( - TraceLog::GetCategoryGroupEnabled("__metadata"), "metadata_event_2", - "metadata_arg_name", std::move(conv2)); - // |AppendAsTraceFormat| should only be called on flush, not when the event - // is added. - ASSERT_EQ(0, num_calls); - EndTraceAndFlush(); - ASSERT_EQ(2, num_calls); - EXPECT_TRUE(FindNamePhaseKeyValue("metadata_event_1", "M", - "metadata_arg_name", "metadata_value")); - EXPECT_TRUE(FindNamePhaseKeyValue("metadata_event_2", "M", - "metadata_arg_name", "metadata_value")); - - // The metadata event should only be adde to the current trace. In this new - // trace, the event should not appear. - BeginTrace(); - EndTraceAndFlush(); - ASSERT_EQ(2, num_calls); -} - -// Test that categories work. -TEST_F(TraceEventTestFixture, Categories) { - // Test that categories that are used can be retrieved whether trace was - // enabled or disabled when the trace event was encountered. - TRACE_EVENT_INSTANT0("c1", "name", TRACE_EVENT_SCOPE_THREAD); - TRACE_EVENT_INSTANT0("c2", "name", TRACE_EVENT_SCOPE_THREAD); - BeginTrace(); - TRACE_EVENT_INSTANT0("c3", "name", TRACE_EVENT_SCOPE_THREAD); - TRACE_EVENT_INSTANT0("c4", "name", TRACE_EVENT_SCOPE_THREAD); - // Category groups containing more than one category. - TRACE_EVENT_INSTANT0("c5,c6", "name", TRACE_EVENT_SCOPE_THREAD); - TRACE_EVENT_INSTANT0("c7,c8", "name", TRACE_EVENT_SCOPE_THREAD); - TRACE_EVENT_INSTANT0(TRACE_DISABLED_BY_DEFAULT("c9"), "name", - TRACE_EVENT_SCOPE_THREAD); - - EndTraceAndFlush(); - std::vector<std::string> cat_groups; - TraceLog::GetInstance()->GetKnownCategoryGroups(&cat_groups); - EXPECT_TRUE(ContainsValue(cat_groups, "c1")); - EXPECT_TRUE(ContainsValue(cat_groups, "c2")); - EXPECT_TRUE(ContainsValue(cat_groups, "c3")); - EXPECT_TRUE(ContainsValue(cat_groups, "c4")); - EXPECT_TRUE(ContainsValue(cat_groups, "c5,c6")); - EXPECT_TRUE(ContainsValue(cat_groups, "c7,c8")); - EXPECT_TRUE(ContainsValue(cat_groups, "disabled-by-default-c9")); - // Make sure metadata isn't returned. - EXPECT_FALSE(ContainsValue(cat_groups, "__metadata")); - - const std::vector<std::string> empty_categories; - std::vector<std::string> included_categories; - std::vector<std::string> excluded_categories; - - // Test that category filtering works. - - // Include nonexistent category -> no events - Clear(); - included_categories.clear(); - TraceLog::GetInstance()->SetEnabled(TraceConfig("not_found823564786", ""), - TraceLog::RECORDING_MODE); - TRACE_EVENT_INSTANT0("cat1", "name", TRACE_EVENT_SCOPE_THREAD); - TRACE_EVENT_INSTANT0("cat2", "name", TRACE_EVENT_SCOPE_THREAD); - EndTraceAndFlush(); - DropTracedMetadataRecords(); - EXPECT_TRUE(trace_parsed_.empty()); - - // Include existent category -> only events of that category - Clear(); - included_categories.clear(); - TraceLog::GetInstance()->SetEnabled(TraceConfig("inc", ""), - TraceLog::RECORDING_MODE); - TRACE_EVENT_INSTANT0("inc", "name", TRACE_EVENT_SCOPE_THREAD); - TRACE_EVENT_INSTANT0("inc2", "name", TRACE_EVENT_SCOPE_THREAD); - EndTraceAndFlush(); - DropTracedMetadataRecords(); - EXPECT_TRUE(FindMatchingValue("cat", "inc")); - EXPECT_FALSE(FindNonMatchingValue("cat", "inc")); - - // Include existent wildcard -> all categories matching wildcard - Clear(); - included_categories.clear(); - TraceLog::GetInstance()->SetEnabled( - TraceConfig("inc_wildcard_*,inc_wildchar_?_end", ""), - TraceLog::RECORDING_MODE); - TRACE_EVENT_INSTANT0("inc_wildcard_abc", "included", - TRACE_EVENT_SCOPE_THREAD); - TRACE_EVENT_INSTANT0("inc_wildcard_", "included", TRACE_EVENT_SCOPE_THREAD); - TRACE_EVENT_INSTANT0("inc_wildchar_x_end", "included", - TRACE_EVENT_SCOPE_THREAD); - TRACE_EVENT_INSTANT0("inc_wildchar_bla_end", "not_inc", - TRACE_EVENT_SCOPE_THREAD); - TRACE_EVENT_INSTANT0("cat1", "not_inc", TRACE_EVENT_SCOPE_THREAD); - TRACE_EVENT_INSTANT0("cat2", "not_inc", TRACE_EVENT_SCOPE_THREAD); - TRACE_EVENT_INSTANT0("inc_wildcard_category,other_category", "included", - TRACE_EVENT_SCOPE_THREAD); - TRACE_EVENT_INSTANT0( - "non_included_category,inc_wildcard_category", "included", - TRACE_EVENT_SCOPE_THREAD); - EndTraceAndFlush(); - EXPECT_TRUE(FindMatchingValue("cat", "inc_wildcard_abc")); - EXPECT_TRUE(FindMatchingValue("cat", "inc_wildcard_")); - EXPECT_TRUE(FindMatchingValue("cat", "inc_wildchar_x_end")); - EXPECT_FALSE(FindMatchingValue("name", "not_inc")); - EXPECT_TRUE(FindMatchingValue("cat", "inc_wildcard_category,other_category")); - EXPECT_TRUE(FindMatchingValue("cat", - "non_included_category,inc_wildcard_category")); - - included_categories.clear(); - - // Exclude nonexistent category -> all events - Clear(); - TraceLog::GetInstance()->SetEnabled(TraceConfig("-not_found823564786", ""), - TraceLog::RECORDING_MODE); - TRACE_EVENT_INSTANT0("cat1", "name", TRACE_EVENT_SCOPE_THREAD); - TRACE_EVENT_INSTANT0("cat2", "name", TRACE_EVENT_SCOPE_THREAD); - TRACE_EVENT_INSTANT0("category1,category2", "name", TRACE_EVENT_SCOPE_THREAD); - EndTraceAndFlush(); - EXPECT_TRUE(FindMatchingValue("cat", "cat1")); - EXPECT_TRUE(FindMatchingValue("cat", "cat2")); - EXPECT_TRUE(FindMatchingValue("cat", "category1,category2")); - - // Exclude existent category -> only events of other categories - Clear(); - TraceLog::GetInstance()->SetEnabled(TraceConfig("-inc", ""), - TraceLog::RECORDING_MODE); - TRACE_EVENT_INSTANT0("inc", "name", TRACE_EVENT_SCOPE_THREAD); - TRACE_EVENT_INSTANT0("inc2", "name", TRACE_EVENT_SCOPE_THREAD); - TRACE_EVENT_INSTANT0("inc2,inc", "name", TRACE_EVENT_SCOPE_THREAD); - TRACE_EVENT_INSTANT0("inc,inc2", "name", TRACE_EVENT_SCOPE_THREAD); - EndTraceAndFlush(); - EXPECT_TRUE(FindMatchingValue("cat", "inc2")); - EXPECT_FALSE(FindMatchingValue("cat", "inc")); - EXPECT_TRUE(FindMatchingValue("cat", "inc2,inc")); - EXPECT_TRUE(FindMatchingValue("cat", "inc,inc2")); - - // Exclude existent wildcard -> all categories not matching wildcard - Clear(); - TraceLog::GetInstance()->SetEnabled( - TraceConfig("-inc_wildcard_*,-inc_wildchar_?_end", ""), - TraceLog::RECORDING_MODE); - TRACE_EVENT_INSTANT0("inc_wildcard_abc", "not_inc", - TRACE_EVENT_SCOPE_THREAD); - TRACE_EVENT_INSTANT0("inc_wildcard_", "not_inc", - TRACE_EVENT_SCOPE_THREAD); - TRACE_EVENT_INSTANT0("inc_wildchar_x_end", "not_inc", - TRACE_EVENT_SCOPE_THREAD); - TRACE_EVENT_INSTANT0("inc_wildchar_bla_end", "included", - TRACE_EVENT_SCOPE_THREAD); - TRACE_EVENT_INSTANT0("cat1", "included", TRACE_EVENT_SCOPE_THREAD); - TRACE_EVENT_INSTANT0("cat2", "included", TRACE_EVENT_SCOPE_THREAD); - EndTraceAndFlush(); - EXPECT_TRUE(FindMatchingValue("cat", "inc_wildchar_bla_end")); - EXPECT_TRUE(FindMatchingValue("cat", "cat1")); - EXPECT_TRUE(FindMatchingValue("cat", "cat2")); - EXPECT_FALSE(FindMatchingValue("name", "not_inc")); -} - - -// Test ASYNC_BEGIN/END events -TEST_F(TraceEventTestFixture, AsyncBeginEndEvents) { - BeginTrace(); - - unsigned long long id = 0xfeedbeeffeedbeefull; - TRACE_EVENT_ASYNC_BEGIN0("cat", "name1", id); - TRACE_EVENT_ASYNC_STEP_INTO0("cat", "name1", id, "step1"); - TRACE_EVENT_ASYNC_END0("cat", "name1", id); - TRACE_EVENT_BEGIN0("cat", "name2"); - TRACE_EVENT_ASYNC_BEGIN0("cat", "name3", 0); - TRACE_EVENT_ASYNC_STEP_PAST0("cat", "name3", 0, "step2"); - - EndTraceAndFlush(); - - EXPECT_TRUE(FindNamePhase("name1", "S")); - EXPECT_TRUE(FindNamePhase("name1", "T")); - EXPECT_TRUE(FindNamePhase("name1", "F")); - - std::string id_str; - StringAppendF(&id_str, "0x%llx", id); - - EXPECT_TRUE(FindNamePhaseKeyValue("name1", "S", "id", id_str.c_str())); - EXPECT_TRUE(FindNamePhaseKeyValue("name1", "T", "id", id_str.c_str())); - EXPECT_TRUE(FindNamePhaseKeyValue("name1", "F", "id", id_str.c_str())); - EXPECT_TRUE(FindNamePhaseKeyValue("name3", "S", "id", "0x0")); - EXPECT_TRUE(FindNamePhaseKeyValue("name3", "p", "id", "0x0")); - - // BEGIN events should not have id - EXPECT_FALSE(FindNamePhaseKeyValue("name2", "B", "id", "0")); -} - -// Test ASYNC_BEGIN/END events -TEST_F(TraceEventTestFixture, AsyncBeginEndPointerMangling) { - void* ptr = this; - - TraceLog::GetInstance()->SetProcessID(100); - BeginTrace(); - TRACE_EVENT_ASYNC_BEGIN0("cat", "name1", ptr); - TRACE_EVENT_ASYNC_BEGIN0("cat", "name2", ptr); - EndTraceAndFlush(); - - TraceLog::GetInstance()->SetProcessID(200); - BeginTrace(); - TRACE_EVENT_ASYNC_END0("cat", "name1", ptr); - EndTraceAndFlush(); - - DictionaryValue* async_begin = FindNamePhase("name1", "S"); - DictionaryValue* async_begin2 = FindNamePhase("name2", "S"); - DictionaryValue* async_end = FindNamePhase("name1", "F"); - EXPECT_TRUE(async_begin); - EXPECT_TRUE(async_begin2); - EXPECT_TRUE(async_end); - - Value* value = NULL; - std::string async_begin_id_str; - std::string async_begin2_id_str; - std::string async_end_id_str; - ASSERT_TRUE(async_begin->Get("id", &value)); - ASSERT_TRUE(value->GetAsString(&async_begin_id_str)); - ASSERT_TRUE(async_begin2->Get("id", &value)); - ASSERT_TRUE(value->GetAsString(&async_begin2_id_str)); - ASSERT_TRUE(async_end->Get("id", &value)); - ASSERT_TRUE(value->GetAsString(&async_end_id_str)); - - EXPECT_STREQ(async_begin_id_str.c_str(), async_begin2_id_str.c_str()); - EXPECT_STRNE(async_begin_id_str.c_str(), async_end_id_str.c_str()); -} - -// Test that static strings are not copied. -TEST_F(TraceEventTestFixture, StaticStringVsString) { - TraceLog* tracer = TraceLog::GetInstance(); - // Make sure old events are flushed: - EXPECT_EQ(0u, tracer->GetStatus().event_count); - const unsigned char* category_group_enabled = - TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED("cat"); - - { - BeginTrace(); - // Test that string arguments are copied. - TraceEventHandle handle1 = - trace_event_internal::AddTraceEvent( - TRACE_EVENT_PHASE_INSTANT, category_group_enabled, "name1", - trace_event_internal::kGlobalScope, trace_event_internal::kNoId, - 0, trace_event_internal::kNoId, - "arg1", std::string("argval"), "arg2", std::string("argval")); - // Test that static TRACE_STR_COPY string arguments are copied. - TraceEventHandle handle2 = - trace_event_internal::AddTraceEvent( - TRACE_EVENT_PHASE_INSTANT, category_group_enabled, "name2", - trace_event_internal::kGlobalScope, trace_event_internal::kNoId, - 0, trace_event_internal::kNoId, - "arg1", TRACE_STR_COPY("argval"), - "arg2", TRACE_STR_COPY("argval")); - EXPECT_GT(tracer->GetStatus().event_count, 1u); - const TraceEvent* event1 = tracer->GetEventByHandle(handle1); - const TraceEvent* event2 = tracer->GetEventByHandle(handle2); - ASSERT_TRUE(event1); - ASSERT_TRUE(event2); - EXPECT_STREQ("name1", event1->name()); - EXPECT_STREQ("name2", event2->name()); - EXPECT_TRUE(event1->parameter_copy_storage() != NULL); - EXPECT_TRUE(event2->parameter_copy_storage() != NULL); - EXPECT_GT(event1->parameter_copy_storage()->size(), 0u); - EXPECT_GT(event2->parameter_copy_storage()->size(), 0u); - EndTraceAndFlush(); - } - - { - BeginTrace(); - // Test that static literal string arguments are not copied. - TraceEventHandle handle1 = - trace_event_internal::AddTraceEvent( - TRACE_EVENT_PHASE_INSTANT, category_group_enabled, "name1", - trace_event_internal::kGlobalScope, trace_event_internal::kNoId, - 0, trace_event_internal::kNoId, - "arg1", "argval", "arg2", "argval"); - // Test that static TRACE_STR_COPY NULL string arguments are not copied. - const char* str1 = NULL; - const char* str2 = NULL; - TraceEventHandle handle2 = - trace_event_internal::AddTraceEvent( - TRACE_EVENT_PHASE_INSTANT, category_group_enabled, "name2", - trace_event_internal::kGlobalScope, trace_event_internal::kNoId, - 0, trace_event_internal::kNoId, - "arg1", TRACE_STR_COPY(str1), - "arg2", TRACE_STR_COPY(str2)); - EXPECT_GT(tracer->GetStatus().event_count, 1u); - const TraceEvent* event1 = tracer->GetEventByHandle(handle1); - const TraceEvent* event2 = tracer->GetEventByHandle(handle2); - ASSERT_TRUE(event1); - ASSERT_TRUE(event2); - EXPECT_STREQ("name1", event1->name()); - EXPECT_STREQ("name2", event2->name()); - EXPECT_TRUE(event1->parameter_copy_storage() == NULL); - EXPECT_TRUE(event2->parameter_copy_storage() == NULL); - EndTraceAndFlush(); - } -} - -// Test that data sent from other threads is gathered -TEST_F(TraceEventTestFixture, DataCapturedOnThread) { - BeginTrace(); - - Thread thread("1"); - WaitableEvent task_complete_event(WaitableEvent::ResetPolicy::AUTOMATIC, - WaitableEvent::InitialState::NOT_SIGNALED); - thread.Start(); - - thread.task_runner()->PostTask( - FROM_HERE, base::Bind(&TraceWithAllMacroVariants, &task_complete_event)); - task_complete_event.Wait(); - thread.Stop(); - - EndTraceAndFlush(); - ValidateAllTraceMacrosCreatedData(trace_parsed_); -} - -// Test that data sent from multiple threads is gathered -TEST_F(TraceEventTestFixture, DataCapturedManyThreads) { - BeginTrace(); - - const int num_threads = 4; - const int num_events = 4000; - Thread* threads[num_threads]; - WaitableEvent* task_complete_events[num_threads]; - for (int i = 0; i < num_threads; i++) { - threads[i] = new Thread(StringPrintf("Thread %d", i)); - task_complete_events[i] = - new WaitableEvent(WaitableEvent::ResetPolicy::AUTOMATIC, - WaitableEvent::InitialState::NOT_SIGNALED); - threads[i]->Start(); - threads[i]->task_runner()->PostTask( - FROM_HERE, base::Bind(&TraceManyInstantEvents, i, num_events, - task_complete_events[i])); - } - - for (int i = 0; i < num_threads; i++) { - task_complete_events[i]->Wait(); - } - - // Let half of the threads end before flush. - for (int i = 0; i < num_threads / 2; i++) { - threads[i]->Stop(); - delete threads[i]; - delete task_complete_events[i]; - } - - EndTraceAndFlushInThreadWithMessageLoop(); - ValidateInstantEventPresentOnEveryThread(trace_parsed_, - num_threads, num_events); - - // Let the other half of the threads end after flush. - for (int i = num_threads / 2; i < num_threads; i++) { - threads[i]->Stop(); - delete threads[i]; - delete task_complete_events[i]; - } -} - -// Test that thread and process names show up in the trace -TEST_F(TraceEventTestFixture, ThreadNames) { - // Create threads before we enable tracing to make sure - // that tracelog still captures them. - const int kNumThreads = 4; - const int kNumEvents = 10; - Thread* threads[kNumThreads]; - PlatformThreadId thread_ids[kNumThreads]; - for (int i = 0; i < kNumThreads; i++) - threads[i] = new Thread(StringPrintf("Thread %d", i)); - - // Enable tracing. - BeginTrace(); - - // Now run some trace code on these threads. - WaitableEvent* task_complete_events[kNumThreads]; - for (int i = 0; i < kNumThreads; i++) { - task_complete_events[i] = - new WaitableEvent(WaitableEvent::ResetPolicy::AUTOMATIC, - WaitableEvent::InitialState::NOT_SIGNALED); - threads[i]->Start(); - thread_ids[i] = threads[i]->GetThreadId(); - threads[i]->task_runner()->PostTask( - FROM_HERE, base::Bind(&TraceManyInstantEvents, i, kNumEvents, - task_complete_events[i])); - } - for (int i = 0; i < kNumThreads; i++) { - task_complete_events[i]->Wait(); - } - - // Shut things down. - for (int i = 0; i < kNumThreads; i++) { - threads[i]->Stop(); - delete threads[i]; - delete task_complete_events[i]; - } - - EndTraceAndFlush(); - - std::string tmp; - int tmp_int; - const DictionaryValue* item; - - // Make sure we get thread name metadata. - // Note, the test suite may have created a ton of threads. - // So, we'll have thread names for threads we didn't create. - std::vector<const DictionaryValue*> items = - FindTraceEntries(trace_parsed_, "thread_name"); - for (int i = 0; i < static_cast<int>(items.size()); i++) { - item = items[i]; - ASSERT_TRUE(item); - EXPECT_TRUE(item->GetInteger("tid", &tmp_int)); - - // See if this thread name is one of the threads we just created - for (int j = 0; j < kNumThreads; j++) { - if (static_cast<int>(thread_ids[j]) != tmp_int) - continue; - - std::string expected_name = StringPrintf("Thread %d", j); - EXPECT_TRUE(item->GetString("ph", &tmp) && tmp == "M"); - EXPECT_TRUE(item->GetInteger("pid", &tmp_int) && - tmp_int == static_cast<int>(base::GetCurrentProcId())); - // If the thread name changes or the tid gets reused, the name will be - // a comma-separated list of thread names, so look for a substring. - EXPECT_TRUE(item->GetString("args.name", &tmp) && - tmp.find(expected_name) != std::string::npos); - } - } -} - -TEST_F(TraceEventTestFixture, ThreadNameChanges) { - BeginTrace(); - - PlatformThread::SetName(""); - TRACE_EVENT_INSTANT0("drink", "water", TRACE_EVENT_SCOPE_THREAD); - - PlatformThread::SetName("cafe"); - TRACE_EVENT_INSTANT0("drink", "coffee", TRACE_EVENT_SCOPE_THREAD); - - PlatformThread::SetName("shop"); - // No event here, so won't appear in combined name. - - PlatformThread::SetName("pub"); - TRACE_EVENT_INSTANT0("drink", "beer", TRACE_EVENT_SCOPE_THREAD); - TRACE_EVENT_INSTANT0("drink", "wine", TRACE_EVENT_SCOPE_THREAD); - - PlatformThread::SetName(" bar"); - TRACE_EVENT_INSTANT0("drink", "whisky", TRACE_EVENT_SCOPE_THREAD); - - EndTraceAndFlush(); - - std::vector<const DictionaryValue*> items = - FindTraceEntries(trace_parsed_, "thread_name"); - EXPECT_EQ(1u, items.size()); - ASSERT_GT(items.size(), 0u); - const DictionaryValue* item = items[0]; - ASSERT_TRUE(item); - int tid; - EXPECT_TRUE(item->GetInteger("tid", &tid)); - EXPECT_EQ(PlatformThread::CurrentId(), static_cast<PlatformThreadId>(tid)); - - std::string expected_name = "cafe,pub, bar"; - std::string tmp; - EXPECT_TRUE(item->GetString("args.name", &tmp)); - EXPECT_EQ(expected_name, tmp); -} - -// Test that the disabled trace categories are included/excluded from the -// trace output correctly. -TEST_F(TraceEventTestFixture, DisabledCategories) { - BeginTrace(); - TRACE_EVENT_INSTANT0(TRACE_DISABLED_BY_DEFAULT("cc"), "first", - TRACE_EVENT_SCOPE_THREAD); - TRACE_EVENT_INSTANT0("included", "first", TRACE_EVENT_SCOPE_THREAD); - EndTraceAndFlush(); - { - const DictionaryValue* item = NULL; - ListValue& trace_parsed = trace_parsed_; - EXPECT_NOT_FIND_("disabled-by-default-cc"); - EXPECT_FIND_("included"); - } - Clear(); - - BeginSpecificTrace("disabled-by-default-cc"); - TRACE_EVENT_INSTANT0(TRACE_DISABLED_BY_DEFAULT("cc"), "second", - TRACE_EVENT_SCOPE_THREAD); - TRACE_EVENT_INSTANT0("other_included", "second", TRACE_EVENT_SCOPE_THREAD); - EndTraceAndFlush(); - - { - const DictionaryValue* item = NULL; - ListValue& trace_parsed = trace_parsed_; - EXPECT_FIND_("disabled-by-default-cc"); - EXPECT_FIND_("other_included"); - } - - Clear(); - - BeginSpecificTrace("other_included"); - TRACE_EVENT_INSTANT0(TRACE_DISABLED_BY_DEFAULT("cc") ",other_included", - "first", TRACE_EVENT_SCOPE_THREAD); - TRACE_EVENT_INSTANT0("other_included," TRACE_DISABLED_BY_DEFAULT("cc"), - "second", TRACE_EVENT_SCOPE_THREAD); - EndTraceAndFlush(); - - { - const DictionaryValue* item = NULL; - ListValue& trace_parsed = trace_parsed_; - EXPECT_FIND_("disabled-by-default-cc,other_included"); - EXPECT_FIND_("other_included,disabled-by-default-cc"); - } -} - -TEST_F(TraceEventTestFixture, NormallyNoDeepCopy) { - // Test that the TRACE_EVENT macros do not deep-copy their string. If they - // do so it may indicate a performance regression, but more-over it would - // make the DEEP_COPY overloads redundant. - std::string name_string("event name"); - - BeginTrace(); - TRACE_EVENT_INSTANT0("category", name_string.c_str(), - TRACE_EVENT_SCOPE_THREAD); - - // Modify the string in place (a wholesale reassignment may leave the old - // string intact on the heap). - name_string[0] = '@'; - - EndTraceAndFlush(); - - EXPECT_FALSE(FindTraceEntry(trace_parsed_, "event name")); - EXPECT_TRUE(FindTraceEntry(trace_parsed_, name_string.c_str())); -} - -TEST_F(TraceEventTestFixture, DeepCopy) { - static const char kOriginalName1[] = "name1"; - static const char kOriginalName2[] = "name2"; - static const char kOriginalName3[] = "name3"; - std::string name1(kOriginalName1); - std::string name2(kOriginalName2); - std::string name3(kOriginalName3); - std::string arg1("arg1"); - std::string arg2("arg2"); - std::string val1("val1"); - std::string val2("val2"); - - BeginTrace(); - TRACE_EVENT_COPY_INSTANT0("category", name1.c_str(), - TRACE_EVENT_SCOPE_THREAD); - TRACE_EVENT_COPY_BEGIN1("category", name2.c_str(), - arg1.c_str(), 5); - TRACE_EVENT_COPY_END2("category", name3.c_str(), - arg1.c_str(), val1, - arg2.c_str(), val2); - - // As per NormallyNoDeepCopy, modify the strings in place. - name1[0] = name2[0] = name3[0] = arg1[0] = arg2[0] = val1[0] = val2[0] = '@'; - - EndTraceAndFlush(); - - EXPECT_FALSE(FindTraceEntry(trace_parsed_, name1.c_str())); - EXPECT_FALSE(FindTraceEntry(trace_parsed_, name2.c_str())); - EXPECT_FALSE(FindTraceEntry(trace_parsed_, name3.c_str())); - - const DictionaryValue* entry1 = FindTraceEntry(trace_parsed_, kOriginalName1); - const DictionaryValue* entry2 = FindTraceEntry(trace_parsed_, kOriginalName2); - const DictionaryValue* entry3 = FindTraceEntry(trace_parsed_, kOriginalName3); - ASSERT_TRUE(entry1); - ASSERT_TRUE(entry2); - ASSERT_TRUE(entry3); - - int i; - EXPECT_FALSE(entry2->GetInteger("args.@rg1", &i)); - EXPECT_TRUE(entry2->GetInteger("args.arg1", &i)); - EXPECT_EQ(5, i); - - std::string s; - EXPECT_TRUE(entry3->GetString("args.arg1", &s)); - EXPECT_EQ("val1", s); - EXPECT_TRUE(entry3->GetString("args.arg2", &s)); - EXPECT_EQ("val2", s); -} - -// Test that TraceResultBuffer outputs the correct result whether it is added -// in chunks or added all at once. -TEST_F(TraceEventTestFixture, TraceResultBuffer) { - Clear(); - - trace_buffer_.Start(); - trace_buffer_.AddFragment("bla1"); - trace_buffer_.AddFragment("bla2"); - trace_buffer_.AddFragment("bla3,bla4"); - trace_buffer_.Finish(); - EXPECT_STREQ(json_output_.json_output.c_str(), "[bla1,bla2,bla3,bla4]"); - - Clear(); - - trace_buffer_.Start(); - trace_buffer_.AddFragment("bla1,bla2,bla3,bla4"); - trace_buffer_.Finish(); - EXPECT_STREQ(json_output_.json_output.c_str(), "[bla1,bla2,bla3,bla4]"); -} - -// Test that trace_event parameters are not evaluated if the tracing -// system is disabled. -TEST_F(TraceEventTestFixture, TracingIsLazy) { - BeginTrace(); - - int a = 0; - TRACE_EVENT_INSTANT1("category", "test", TRACE_EVENT_SCOPE_THREAD, "a", a++); - EXPECT_EQ(1, a); - - TraceLog::GetInstance()->SetDisabled(); - - TRACE_EVENT_INSTANT1("category", "test", TRACE_EVENT_SCOPE_THREAD, "a", a++); - EXPECT_EQ(1, a); - - EndTraceAndFlush(); -} - -TEST_F(TraceEventTestFixture, TraceEnableDisable) { - TraceLog* trace_log = TraceLog::GetInstance(); - TraceConfig tc_inc_all("*", ""); - trace_log->SetEnabled(tc_inc_all, TraceLog::RECORDING_MODE); - EXPECT_TRUE(trace_log->IsEnabled()); - trace_log->SetDisabled(); - EXPECT_FALSE(trace_log->IsEnabled()); - - trace_log->SetEnabled(tc_inc_all, TraceLog::RECORDING_MODE); - EXPECT_TRUE(trace_log->IsEnabled()); - const std::vector<std::string> empty; - trace_log->SetEnabled(TraceConfig(), TraceLog::RECORDING_MODE); - EXPECT_TRUE(trace_log->IsEnabled()); - trace_log->SetDisabled(); - EXPECT_FALSE(trace_log->IsEnabled()); - trace_log->SetDisabled(); - EXPECT_FALSE(trace_log->IsEnabled()); -} - -TEST_F(TraceEventTestFixture, TraceCategoriesAfterNestedEnable) { - TraceLog* trace_log = TraceLog::GetInstance(); - trace_log->SetEnabled(TraceConfig("foo,bar", ""), TraceLog::RECORDING_MODE); - EXPECT_TRUE(*trace_log->GetCategoryGroupEnabled("foo")); - EXPECT_TRUE(*trace_log->GetCategoryGroupEnabled("bar")); - EXPECT_FALSE(*trace_log->GetCategoryGroupEnabled("baz")); - trace_log->SetEnabled(TraceConfig("foo2", ""), TraceLog::RECORDING_MODE); - EXPECT_TRUE(*trace_log->GetCategoryGroupEnabled("foo2")); - EXPECT_FALSE(*trace_log->GetCategoryGroupEnabled("baz")); - // The "" becomes the default catergory set when applied. - trace_log->SetEnabled(TraceConfig(), TraceLog::RECORDING_MODE); - EXPECT_TRUE(*trace_log->GetCategoryGroupEnabled("foo")); - EXPECT_TRUE(*trace_log->GetCategoryGroupEnabled("baz")); - EXPECT_STREQ( - "", - trace_log->GetCurrentTraceConfig().ToCategoryFilterString().c_str()); - trace_log->SetDisabled(); - trace_log->SetDisabled(); - trace_log->SetDisabled(); - EXPECT_FALSE(*trace_log->GetCategoryGroupEnabled("foo")); - EXPECT_FALSE(*trace_log->GetCategoryGroupEnabled("baz")); - - trace_log->SetEnabled(TraceConfig("-foo,-bar", ""), TraceLog::RECORDING_MODE); - EXPECT_FALSE(*trace_log->GetCategoryGroupEnabled("foo")); - EXPECT_TRUE(*trace_log->GetCategoryGroupEnabled("baz")); - trace_log->SetEnabled(TraceConfig("moo", ""), TraceLog::RECORDING_MODE); - EXPECT_TRUE(*trace_log->GetCategoryGroupEnabled("baz")); - EXPECT_TRUE(*trace_log->GetCategoryGroupEnabled("moo")); - EXPECT_FALSE(*trace_log->GetCategoryGroupEnabled("foo")); - EXPECT_STREQ( - "-foo,-bar", - trace_log->GetCurrentTraceConfig().ToCategoryFilterString().c_str()); - trace_log->SetDisabled(); - trace_log->SetDisabled(); - - // Make sure disabled categories aren't cleared if we set in the second. - trace_log->SetEnabled(TraceConfig("disabled-by-default-cc,foo", ""), - TraceLog::RECORDING_MODE); - EXPECT_FALSE(*trace_log->GetCategoryGroupEnabled("bar")); - trace_log->SetEnabled(TraceConfig("disabled-by-default-gpu", ""), - TraceLog::RECORDING_MODE); - EXPECT_TRUE(*trace_log->GetCategoryGroupEnabled("disabled-by-default-cc")); - EXPECT_TRUE(*trace_log->GetCategoryGroupEnabled("disabled-by-default-gpu")); - EXPECT_TRUE(*trace_log->GetCategoryGroupEnabled("bar")); - EXPECT_STREQ( - "disabled-by-default-cc,disabled-by-default-gpu", - trace_log->GetCurrentTraceConfig().ToCategoryFilterString().c_str()); - trace_log->SetDisabled(); - trace_log->SetDisabled(); -} - -TEST_F(TraceEventTestFixture, TraceWithDefaultCategoryFilters) { - TraceLog* trace_log = TraceLog::GetInstance(); - - trace_log->SetEnabled(TraceConfig(), TraceLog::RECORDING_MODE); - CheckTraceDefaultCategoryFilters(*trace_log); - trace_log->SetDisabled(); - - trace_log->SetEnabled(TraceConfig("", ""), TraceLog::RECORDING_MODE); - CheckTraceDefaultCategoryFilters(*trace_log); - trace_log->SetDisabled(); - - trace_log->SetEnabled(TraceConfig("*", ""), TraceLog::RECORDING_MODE); - CheckTraceDefaultCategoryFilters(*trace_log); - trace_log->SetDisabled(); - - trace_log->SetEnabled(TraceConfig(""), TraceLog::RECORDING_MODE); - CheckTraceDefaultCategoryFilters(*trace_log); - trace_log->SetDisabled(); -} - -TEST_F(TraceEventTestFixture, TraceWithDisabledByDefaultCategoryFilters) { - TraceLog* trace_log = TraceLog::GetInstance(); - - trace_log->SetEnabled(TraceConfig("foo,disabled-by-default-foo", ""), - TraceLog::RECORDING_MODE); - EXPECT_TRUE(*trace_log->GetCategoryGroupEnabled("foo")); - EXPECT_TRUE(*trace_log->GetCategoryGroupEnabled("disabled-by-default-foo")); - EXPECT_FALSE(*trace_log->GetCategoryGroupEnabled("bar")); - EXPECT_FALSE(*trace_log->GetCategoryGroupEnabled("disabled-by-default-bar")); - trace_log->SetDisabled(); - - // Enabling only the disabled-by-default-* category means the default ones - // are also enabled. - trace_log->SetEnabled(TraceConfig("disabled-by-default-foo", ""), - TraceLog::RECORDING_MODE); - EXPECT_TRUE(*trace_log->GetCategoryGroupEnabled("disabled-by-default-foo")); - EXPECT_TRUE(*trace_log->GetCategoryGroupEnabled("foo")); - EXPECT_TRUE(*trace_log->GetCategoryGroupEnabled("bar")); - EXPECT_FALSE(*trace_log->GetCategoryGroupEnabled("disabled-by-default-bar")); - trace_log->SetDisabled(); -} - -class MyData : public ConvertableToTraceFormat { - public: - MyData() {} - ~MyData() override {} - - void AppendAsTraceFormat(std::string* out) const override { - out->append("{\"foo\":1}"); - } - - private: - DISALLOW_COPY_AND_ASSIGN(MyData); -}; - -TEST_F(TraceEventTestFixture, ConvertableTypes) { - TraceLog::GetInstance()->SetEnabled(TraceConfig(kRecordAllCategoryFilter, ""), - TraceLog::RECORDING_MODE); - - std::unique_ptr<ConvertableToTraceFormat> data(new MyData()); - std::unique_ptr<ConvertableToTraceFormat> data1(new MyData()); - std::unique_ptr<ConvertableToTraceFormat> data2(new MyData()); - TRACE_EVENT1("foo", "bar", "data", std::move(data)); - TRACE_EVENT2("foo", "baz", "data1", std::move(data1), "data2", - std::move(data2)); - - // Check that std::unique_ptr<DerivedClassOfConvertable> are properly treated - // as - // convertable and not accidentally casted to bool. - std::unique_ptr<MyData> convertData1(new MyData()); - std::unique_ptr<MyData> convertData2(new MyData()); - std::unique_ptr<MyData> convertData3(new MyData()); - std::unique_ptr<MyData> convertData4(new MyData()); - TRACE_EVENT2("foo", "string_first", "str", "string value 1", "convert", - std::move(convertData1)); - TRACE_EVENT2("foo", "string_second", "convert", std::move(convertData2), - "str", "string value 2"); - TRACE_EVENT2("foo", "both_conv", "convert1", std::move(convertData3), - "convert2", std::move(convertData4)); - EndTraceAndFlush(); - - // One arg version. - DictionaryValue* dict = FindNamePhase("bar", "X"); - ASSERT_TRUE(dict); - - const DictionaryValue* args_dict = NULL; - dict->GetDictionary("args", &args_dict); - ASSERT_TRUE(args_dict); - - const Value* value = NULL; - const DictionaryValue* convertable_dict = NULL; - EXPECT_TRUE(args_dict->Get("data", &value)); - ASSERT_TRUE(value->GetAsDictionary(&convertable_dict)); - - int foo_val; - EXPECT_TRUE(convertable_dict->GetInteger("foo", &foo_val)); - EXPECT_EQ(1, foo_val); - - // Two arg version. - dict = FindNamePhase("baz", "X"); - ASSERT_TRUE(dict); - - args_dict = NULL; - dict->GetDictionary("args", &args_dict); - ASSERT_TRUE(args_dict); - - value = NULL; - convertable_dict = NULL; - EXPECT_TRUE(args_dict->Get("data1", &value)); - ASSERT_TRUE(value->GetAsDictionary(&convertable_dict)); - - value = NULL; - convertable_dict = NULL; - EXPECT_TRUE(args_dict->Get("data2", &value)); - ASSERT_TRUE(value->GetAsDictionary(&convertable_dict)); - - // Convertable with other types. - dict = FindNamePhase("string_first", "X"); - ASSERT_TRUE(dict); - - args_dict = NULL; - dict->GetDictionary("args", &args_dict); - ASSERT_TRUE(args_dict); - - std::string str_value; - EXPECT_TRUE(args_dict->GetString("str", &str_value)); - EXPECT_STREQ("string value 1", str_value.c_str()); - - value = NULL; - convertable_dict = NULL; - foo_val = 0; - EXPECT_TRUE(args_dict->Get("convert", &value)); - ASSERT_TRUE(value->GetAsDictionary(&convertable_dict)); - EXPECT_TRUE(convertable_dict->GetInteger("foo", &foo_val)); - EXPECT_EQ(1, foo_val); - - dict = FindNamePhase("string_second", "X"); - ASSERT_TRUE(dict); - - args_dict = NULL; - dict->GetDictionary("args", &args_dict); - ASSERT_TRUE(args_dict); - - EXPECT_TRUE(args_dict->GetString("str", &str_value)); - EXPECT_STREQ("string value 2", str_value.c_str()); - - value = NULL; - convertable_dict = NULL; - foo_val = 0; - EXPECT_TRUE(args_dict->Get("convert", &value)); - ASSERT_TRUE(value->GetAsDictionary(&convertable_dict)); - EXPECT_TRUE(convertable_dict->GetInteger("foo", &foo_val)); - EXPECT_EQ(1, foo_val); - - dict = FindNamePhase("both_conv", "X"); - ASSERT_TRUE(dict); - - args_dict = NULL; - dict->GetDictionary("args", &args_dict); - ASSERT_TRUE(args_dict); - - value = NULL; - convertable_dict = NULL; - foo_val = 0; - EXPECT_TRUE(args_dict->Get("convert1", &value)); - ASSERT_TRUE(value->GetAsDictionary(&convertable_dict)); - EXPECT_TRUE(args_dict->Get("convert2", &value)); - ASSERT_TRUE(value->GetAsDictionary(&convertable_dict)); -} - -TEST_F(TraceEventTestFixture, PrimitiveArgs) { - TraceLog::GetInstance()->SetEnabled(TraceConfig(kRecordAllCategoryFilter, ""), - TraceLog::RECORDING_MODE); - - TRACE_EVENT1("foo", "event1", "int_one", 1); - TRACE_EVENT1("foo", "event2", "int_neg_ten", -10); - TRACE_EVENT1("foo", "event3", "float_one", 1.0f); - TRACE_EVENT1("foo", "event4", "float_half", .5f); - TRACE_EVENT1("foo", "event5", "float_neghalf", -.5f); - TRACE_EVENT1("foo", "event6", "float_infinity", - std::numeric_limits<float>::infinity()); - TRACE_EVENT1("foo", "event6b", "float_neg_infinity", - -std::numeric_limits<float>::infinity()); - TRACE_EVENT1("foo", "event7", "double_nan", - std::numeric_limits<double>::quiet_NaN()); - void* p = 0; - TRACE_EVENT1("foo", "event8", "pointer_null", p); - p = reinterpret_cast<void*>(0xbadf00d); - TRACE_EVENT1("foo", "event9", "pointer_badf00d", p); - TRACE_EVENT1("foo", "event10", "bool_true", true); - TRACE_EVENT1("foo", "event11", "bool_false", false); - TRACE_EVENT1("foo", "event12", "time_null", - base::Time()); - TRACE_EVENT1("foo", "event13", "time_one", - base::Time::FromInternalValue(1)); - TRACE_EVENT1("foo", "event14", "timeticks_null", - base::TimeTicks()); - TRACE_EVENT1("foo", "event15", "timeticks_one", - base::TimeTicks::FromInternalValue(1)); - EndTraceAndFlush(); - - const DictionaryValue* args_dict = NULL; - DictionaryValue* dict = NULL; - const Value* value = NULL; - std::string str_value; - int int_value; - double double_value; - bool bool_value; - - dict = FindNamePhase("event1", "X"); - ASSERT_TRUE(dict); - dict->GetDictionary("args", &args_dict); - ASSERT_TRUE(args_dict); - EXPECT_TRUE(args_dict->GetInteger("int_one", &int_value)); - EXPECT_EQ(1, int_value); - - dict = FindNamePhase("event2", "X"); - ASSERT_TRUE(dict); - dict->GetDictionary("args", &args_dict); - ASSERT_TRUE(args_dict); - EXPECT_TRUE(args_dict->GetInteger("int_neg_ten", &int_value)); - EXPECT_EQ(-10, int_value); - - // 1f must be serlized to JSON as "1.0" in order to be a double, not an int. - dict = FindNamePhase("event3", "X"); - ASSERT_TRUE(dict); - dict->GetDictionary("args", &args_dict); - ASSERT_TRUE(args_dict); - EXPECT_TRUE(args_dict->Get("float_one", &value)); - EXPECT_TRUE(value->IsType(Value::Type::DOUBLE)); - EXPECT_TRUE(value->GetAsDouble(&double_value)); - EXPECT_EQ(1, double_value); - - // .5f must be serlized to JSON as "0.5". - dict = FindNamePhase("event4", "X"); - ASSERT_TRUE(dict); - dict->GetDictionary("args", &args_dict); - ASSERT_TRUE(args_dict); - EXPECT_TRUE(args_dict->Get("float_half", &value)); - EXPECT_TRUE(value->IsType(Value::Type::DOUBLE)); - EXPECT_TRUE(value->GetAsDouble(&double_value)); - EXPECT_EQ(0.5, double_value); - - // -.5f must be serlized to JSON as "-0.5". - dict = FindNamePhase("event5", "X"); - ASSERT_TRUE(dict); - dict->GetDictionary("args", &args_dict); - ASSERT_TRUE(args_dict); - EXPECT_TRUE(args_dict->Get("float_neghalf", &value)); - EXPECT_TRUE(value->IsType(Value::Type::DOUBLE)); - EXPECT_TRUE(value->GetAsDouble(&double_value)); - EXPECT_EQ(-0.5, double_value); - - // Infinity is serialized to JSON as a string. - dict = FindNamePhase("event6", "X"); - ASSERT_TRUE(dict); - dict->GetDictionary("args", &args_dict); - ASSERT_TRUE(args_dict); - EXPECT_TRUE(args_dict->GetString("float_infinity", &str_value)); - EXPECT_STREQ("Infinity", str_value.c_str()); - dict = FindNamePhase("event6b", "X"); - ASSERT_TRUE(dict); - dict->GetDictionary("args", &args_dict); - ASSERT_TRUE(args_dict); - EXPECT_TRUE(args_dict->GetString("float_neg_infinity", &str_value)); - EXPECT_STREQ("-Infinity", str_value.c_str()); - - // NaN is serialized to JSON as a string. - dict = FindNamePhase("event7", "X"); - ASSERT_TRUE(dict); - dict->GetDictionary("args", &args_dict); - ASSERT_TRUE(args_dict); - EXPECT_TRUE(args_dict->GetString("double_nan", &str_value)); - EXPECT_STREQ("NaN", str_value.c_str()); - - // NULL pointers should be serialized as "0x0". - dict = FindNamePhase("event8", "X"); - ASSERT_TRUE(dict); - dict->GetDictionary("args", &args_dict); - ASSERT_TRUE(args_dict); - EXPECT_TRUE(args_dict->GetString("pointer_null", &str_value)); - EXPECT_STREQ("0x0", str_value.c_str()); - - // Other pointers should be serlized as a hex string. - dict = FindNamePhase("event9", "X"); - ASSERT_TRUE(dict); - dict->GetDictionary("args", &args_dict); - ASSERT_TRUE(args_dict); - EXPECT_TRUE(args_dict->GetString("pointer_badf00d", &str_value)); - EXPECT_STREQ("0xbadf00d", str_value.c_str()); - - dict = FindNamePhase("event10", "X"); - ASSERT_TRUE(dict); - dict->GetDictionary("args", &args_dict); - ASSERT_TRUE(args_dict); - EXPECT_TRUE(args_dict->GetBoolean("bool_true", &bool_value)); - EXPECT_TRUE(bool_value); - - dict = FindNamePhase("event11", "X"); - ASSERT_TRUE(dict); - dict->GetDictionary("args", &args_dict); - ASSERT_TRUE(args_dict); - EXPECT_TRUE(args_dict->GetBoolean("bool_false", &bool_value)); - EXPECT_FALSE(bool_value); - - dict = FindNamePhase("event12", "X"); - ASSERT_TRUE(dict); - dict->GetDictionary("args", &args_dict); - ASSERT_TRUE(args_dict); - EXPECT_TRUE(args_dict->GetInteger("time_null", &int_value)); - EXPECT_EQ(0, int_value); - - dict = FindNamePhase("event13", "X"); - ASSERT_TRUE(dict); - dict->GetDictionary("args", &args_dict); - ASSERT_TRUE(args_dict); - EXPECT_TRUE(args_dict->GetInteger("time_one", &int_value)); - EXPECT_EQ(1, int_value); - - dict = FindNamePhase("event14", "X"); - ASSERT_TRUE(dict); - dict->GetDictionary("args", &args_dict); - ASSERT_TRUE(args_dict); - EXPECT_TRUE(args_dict->GetInteger("timeticks_null", &int_value)); - EXPECT_EQ(0, int_value); - - dict = FindNamePhase("event15", "X"); - ASSERT_TRUE(dict); - dict->GetDictionary("args", &args_dict); - ASSERT_TRUE(args_dict); - EXPECT_TRUE(args_dict->GetInteger("timeticks_one", &int_value)); - EXPECT_EQ(1, int_value); -} - -TEST_F(TraceEventTestFixture, NameIsEscaped) { - TraceLog::GetInstance()->SetEnabled(TraceConfig(kRecordAllCategoryFilter, ""), - TraceLog::RECORDING_MODE); - TRACE_EVENT0("category", "name\\with\\backspaces"); - EndTraceAndFlush(); - - EXPECT_TRUE(FindMatchingValue("cat", "category")); - EXPECT_TRUE(FindMatchingValue("name", "name\\with\\backspaces")); -} - -namespace { - -bool IsArgNameWhitelisted(const char* arg_name) { - return base::MatchPattern(arg_name, "granular_arg_whitelisted"); -} - -bool IsTraceEventArgsWhitelisted(const char* category_group_name, - const char* event_name, - ArgumentNameFilterPredicate* arg_filter) { - if (base::MatchPattern(category_group_name, "toplevel") && - base::MatchPattern(event_name, "*")) { - return true; - } - - if (base::MatchPattern(category_group_name, "benchmark") && - base::MatchPattern(event_name, "granularly_whitelisted")) { - *arg_filter = base::Bind(&IsArgNameWhitelisted); - return true; - } - - return false; -} - -} // namespace - -TEST_F(TraceEventTestFixture, ArgsWhitelisting) { - TraceLog::GetInstance()->SetArgumentFilterPredicate( - base::Bind(&IsTraceEventArgsWhitelisted)); - - TraceLog::GetInstance()->SetEnabled( - TraceConfig(kRecordAllCategoryFilter, "enable-argument-filter"), - TraceLog::RECORDING_MODE); - - TRACE_EVENT1("toplevel", "event1", "int_one", 1); - TRACE_EVENT1("whitewashed", "event2", "int_two", 1); - - TRACE_EVENT2("benchmark", "granularly_whitelisted", - "granular_arg_whitelisted", "whitelisted_value", - "granular_arg_blacklisted", "blacklisted_value"); - - EndTraceAndFlush(); - - const DictionaryValue* args_dict = NULL; - DictionaryValue* dict = NULL; - int int_value; - - dict = FindNamePhase("event1", "X"); - ASSERT_TRUE(dict); - dict->GetDictionary("args", &args_dict); - ASSERT_TRUE(args_dict); - EXPECT_TRUE(args_dict->GetInteger("int_one", &int_value)); - EXPECT_EQ(1, int_value); - - dict = FindNamePhase("event2", "X"); - ASSERT_TRUE(dict); - dict->GetDictionary("args", &args_dict); - ASSERT_TRUE(args_dict); - EXPECT_FALSE(args_dict->GetInteger("int_two", &int_value)); - - std::string args_string; - EXPECT_TRUE(dict->GetString("args", &args_string)); - EXPECT_EQ(args_string, "__stripped__"); - - dict = FindNamePhase("granularly_whitelisted", "X"); - ASSERT_TRUE(dict); - dict->GetDictionary("args", &args_dict); - ASSERT_TRUE(args_dict); - - EXPECT_TRUE(args_dict->GetString("granular_arg_whitelisted", &args_string)); - EXPECT_EQ(args_string, "whitelisted_value"); - - EXPECT_TRUE(args_dict->GetString("granular_arg_blacklisted", &args_string)); - EXPECT_EQ(args_string, "__stripped__"); -} - -TEST_F(TraceEventTestFixture, TraceBufferVectorReportFull) { - TraceLog* trace_log = TraceLog::GetInstance(); - trace_log->SetEnabled( - TraceConfig(kRecordAllCategoryFilter, ""), TraceLog::RECORDING_MODE); - trace_log->logged_events_.reset( - TraceBuffer::CreateTraceBufferVectorOfSize(100)); - do { - TRACE_EVENT_BEGIN_WITH_ID_TID_AND_TIMESTAMP0( - "all", "with_timestamp", 0, 0, TimeTicks::Now()); - TRACE_EVENT_END_WITH_ID_TID_AND_TIMESTAMP0( - "all", "with_timestamp", 0, 0, TimeTicks::Now()); - } while (!trace_log->BufferIsFull()); - - EndTraceAndFlush(); - - const DictionaryValue* trace_full_metadata = NULL; - - trace_full_metadata = FindTraceEntry(trace_parsed_, - "overflowed_at_ts"); - std::string phase; - double buffer_limit_reached_timestamp = 0; - - EXPECT_TRUE(trace_full_metadata); - EXPECT_TRUE(trace_full_metadata->GetString("ph", &phase)); - EXPECT_EQ("M", phase); - EXPECT_TRUE(trace_full_metadata->GetDouble( - "args.overflowed_at_ts", &buffer_limit_reached_timestamp)); - EXPECT_DOUBLE_EQ( - static_cast<double>( - trace_log->buffer_limit_reached_timestamp_.ToInternalValue()), - buffer_limit_reached_timestamp); - - // Test that buffer_limit_reached_timestamp's value is between the timestamp - // of the last trace event and current time. - DropTracedMetadataRecords(); - const DictionaryValue* last_trace_event = NULL; - double last_trace_event_timestamp = 0; - EXPECT_TRUE(trace_parsed_.GetDictionary(trace_parsed_.GetSize() - 1, - &last_trace_event)); - EXPECT_TRUE(last_trace_event->GetDouble("ts", &last_trace_event_timestamp)); - EXPECT_LE(last_trace_event_timestamp, buffer_limit_reached_timestamp); - EXPECT_LE(buffer_limit_reached_timestamp, - trace_log->OffsetNow().ToInternalValue()); -} - -TEST_F(TraceEventTestFixture, TraceBufferRingBufferGetReturnChunk) { - TraceLog::GetInstance()->SetEnabled( - TraceConfig(kRecordAllCategoryFilter, RECORD_CONTINUOUSLY), - TraceLog::RECORDING_MODE); - TraceBuffer* buffer = TraceLog::GetInstance()->trace_buffer(); - size_t capacity = buffer->Capacity(); - size_t num_chunks = capacity / TraceBufferChunk::kTraceBufferChunkSize; - uint32_t last_seq = 0; - size_t chunk_index; - EXPECT_EQ(0u, buffer->Size()); - - std::unique_ptr<TraceBufferChunk* []> chunks( - new TraceBufferChunk*[num_chunks]); - for (size_t i = 0; i < num_chunks; ++i) { - chunks[i] = buffer->GetChunk(&chunk_index).release(); - EXPECT_TRUE(chunks[i]); - EXPECT_EQ(i, chunk_index); - EXPECT_GT(chunks[i]->seq(), last_seq); - EXPECT_EQ((i + 1) * TraceBufferChunk::kTraceBufferChunkSize, - buffer->Size()); - last_seq = chunks[i]->seq(); - } - - // Ring buffer is never full. - EXPECT_FALSE(buffer->IsFull()); - - // Return all chunks in original order. - for (size_t i = 0; i < num_chunks; ++i) - buffer->ReturnChunk(i, std::unique_ptr<TraceBufferChunk>(chunks[i])); - - // Should recycle the chunks in the returned order. - for (size_t i = 0; i < num_chunks; ++i) { - chunks[i] = buffer->GetChunk(&chunk_index).release(); - EXPECT_TRUE(chunks[i]); - EXPECT_EQ(i, chunk_index); - EXPECT_GT(chunks[i]->seq(), last_seq); - last_seq = chunks[i]->seq(); - } - - // Return all chunks in reverse order. - for (size_t i = 0; i < num_chunks; ++i) { - buffer->ReturnChunk(num_chunks - i - 1, std::unique_ptr<TraceBufferChunk>( - chunks[num_chunks - i - 1])); - } - - // Should recycle the chunks in the returned order. - for (size_t i = 0; i < num_chunks; ++i) { - chunks[i] = buffer->GetChunk(&chunk_index).release(); - EXPECT_TRUE(chunks[i]); - EXPECT_EQ(num_chunks - i - 1, chunk_index); - EXPECT_GT(chunks[i]->seq(), last_seq); - last_seq = chunks[i]->seq(); - } - - for (size_t i = 0; i < num_chunks; ++i) - buffer->ReturnChunk(i, std::unique_ptr<TraceBufferChunk>(chunks[i])); - - TraceLog::GetInstance()->SetDisabled(); -} - -TEST_F(TraceEventTestFixture, TraceBufferRingBufferHalfIteration) { - TraceLog::GetInstance()->SetEnabled( - TraceConfig(kRecordAllCategoryFilter, RECORD_CONTINUOUSLY), - TraceLog::RECORDING_MODE); - TraceBuffer* buffer = TraceLog::GetInstance()->trace_buffer(); - size_t capacity = buffer->Capacity(); - size_t num_chunks = capacity / TraceBufferChunk::kTraceBufferChunkSize; - size_t chunk_index; - EXPECT_EQ(0u, buffer->Size()); - EXPECT_FALSE(buffer->NextChunk()); - - size_t half_chunks = num_chunks / 2; - std::unique_ptr<TraceBufferChunk* []> chunks( - new TraceBufferChunk*[half_chunks]); - - for (size_t i = 0; i < half_chunks; ++i) { - chunks[i] = buffer->GetChunk(&chunk_index).release(); - EXPECT_TRUE(chunks[i]); - EXPECT_EQ(i, chunk_index); - } - for (size_t i = 0; i < half_chunks; ++i) - buffer->ReturnChunk(i, std::unique_ptr<TraceBufferChunk>(chunks[i])); - - for (size_t i = 0; i < half_chunks; ++i) - EXPECT_EQ(chunks[i], buffer->NextChunk()); - EXPECT_FALSE(buffer->NextChunk()); - TraceLog::GetInstance()->SetDisabled(); -} - -TEST_F(TraceEventTestFixture, TraceBufferRingBufferFullIteration) { - TraceLog::GetInstance()->SetEnabled( - TraceConfig(kRecordAllCategoryFilter, RECORD_CONTINUOUSLY), - TraceLog::RECORDING_MODE); - TraceBuffer* buffer = TraceLog::GetInstance()->trace_buffer(); - size_t capacity = buffer->Capacity(); - size_t num_chunks = capacity / TraceBufferChunk::kTraceBufferChunkSize; - size_t chunk_index; - EXPECT_EQ(0u, buffer->Size()); - EXPECT_FALSE(buffer->NextChunk()); - - std::unique_ptr<TraceBufferChunk* []> chunks( - new TraceBufferChunk*[num_chunks]); - - for (size_t i = 0; i < num_chunks; ++i) { - chunks[i] = buffer->GetChunk(&chunk_index).release(); - EXPECT_TRUE(chunks[i]); - EXPECT_EQ(i, chunk_index); - } - for (size_t i = 0; i < num_chunks; ++i) - buffer->ReturnChunk(i, std::unique_ptr<TraceBufferChunk>(chunks[i])); - - for (size_t i = 0; i < num_chunks; ++i) - EXPECT_TRUE(chunks[i] == buffer->NextChunk()); - EXPECT_FALSE(buffer->NextChunk()); - TraceLog::GetInstance()->SetDisabled(); -} - -TEST_F(TraceEventTestFixture, TraceRecordAsMuchAsPossibleMode) { - TraceLog::GetInstance()->SetEnabled( - TraceConfig(kRecordAllCategoryFilter, RECORD_AS_MUCH_AS_POSSIBLE), - TraceLog::RECORDING_MODE); - TraceBuffer* buffer = TraceLog::GetInstance()->trace_buffer(); - EXPECT_EQ(512000000UL, buffer->Capacity()); - TraceLog::GetInstance()->SetDisabled(); -} - -void BlockUntilStopped(WaitableEvent* task_start_event, - WaitableEvent* task_stop_event) { - task_start_event->Signal(); - task_stop_event->Wait(); -} - -TEST_F(TraceEventTestFixture, SetCurrentThreadBlocksMessageLoopBeforeTracing) { - BeginTrace(); - - Thread thread("1"); - WaitableEvent task_complete_event(WaitableEvent::ResetPolicy::AUTOMATIC, - WaitableEvent::InitialState::NOT_SIGNALED); - thread.Start(); - thread.task_runner()->PostTask( - FROM_HERE, Bind(&TraceLog::SetCurrentThreadBlocksMessageLoop, - Unretained(TraceLog::GetInstance()))); - - thread.task_runner()->PostTask( - FROM_HERE, Bind(&TraceWithAllMacroVariants, &task_complete_event)); - task_complete_event.Wait(); - - WaitableEvent task_start_event(WaitableEvent::ResetPolicy::AUTOMATIC, - WaitableEvent::InitialState::NOT_SIGNALED); - WaitableEvent task_stop_event(WaitableEvent::ResetPolicy::AUTOMATIC, - WaitableEvent::InitialState::NOT_SIGNALED); - thread.task_runner()->PostTask( - FROM_HERE, Bind(&BlockUntilStopped, &task_start_event, &task_stop_event)); - task_start_event.Wait(); - - EndTraceAndFlush(); - ValidateAllTraceMacrosCreatedData(trace_parsed_); - - task_stop_event.Signal(); - thread.Stop(); -} - -TEST_F(TraceEventTestFixture, ConvertTraceConfigToInternalOptions) { - TraceLog* trace_log = TraceLog::GetInstance(); - EXPECT_EQ(TraceLog::kInternalRecordUntilFull, - trace_log->GetInternalOptionsFromTraceConfig( - TraceConfig(kRecordAllCategoryFilter, RECORD_UNTIL_FULL))); - - EXPECT_EQ(TraceLog::kInternalRecordContinuously, - trace_log->GetInternalOptionsFromTraceConfig( - TraceConfig(kRecordAllCategoryFilter, RECORD_CONTINUOUSLY))); - - EXPECT_EQ(TraceLog::kInternalEchoToConsole, - trace_log->GetInternalOptionsFromTraceConfig( - TraceConfig(kRecordAllCategoryFilter, ECHO_TO_CONSOLE))); - - EXPECT_EQ(TraceLog::kInternalEchoToConsole, - trace_log->GetInternalOptionsFromTraceConfig( - TraceConfig("*", "trace-to-console,enable-systrace"))); -} - -void SetBlockingFlagAndBlockUntilStopped(WaitableEvent* task_start_event, - WaitableEvent* task_stop_event) { - TraceLog::GetInstance()->SetCurrentThreadBlocksMessageLoop(); - BlockUntilStopped(task_start_event, task_stop_event); -} - -TEST_F(TraceEventTestFixture, SetCurrentThreadBlocksMessageLoopAfterTracing) { - BeginTrace(); - - Thread thread("1"); - WaitableEvent task_complete_event(WaitableEvent::ResetPolicy::AUTOMATIC, - WaitableEvent::InitialState::NOT_SIGNALED); - thread.Start(); - - thread.task_runner()->PostTask( - FROM_HERE, Bind(&TraceWithAllMacroVariants, &task_complete_event)); - task_complete_event.Wait(); - - WaitableEvent task_start_event(WaitableEvent::ResetPolicy::AUTOMATIC, - WaitableEvent::InitialState::NOT_SIGNALED); - WaitableEvent task_stop_event(WaitableEvent::ResetPolicy::AUTOMATIC, - WaitableEvent::InitialState::NOT_SIGNALED); - thread.task_runner()->PostTask( - FROM_HERE, Bind(&SetBlockingFlagAndBlockUntilStopped, &task_start_event, - &task_stop_event)); - task_start_event.Wait(); - - EndTraceAndFlush(); - ValidateAllTraceMacrosCreatedData(trace_parsed_); - - task_stop_event.Signal(); - thread.Stop(); -} - -TEST_F(TraceEventTestFixture, ThreadOnceBlocking) { - BeginTrace(); - - Thread thread("1"); - WaitableEvent task_complete_event(WaitableEvent::ResetPolicy::AUTOMATIC, - WaitableEvent::InitialState::NOT_SIGNALED); - thread.Start(); - - thread.task_runner()->PostTask( - FROM_HERE, Bind(&TraceWithAllMacroVariants, &task_complete_event)); - task_complete_event.Wait(); - task_complete_event.Reset(); - - WaitableEvent task_start_event(WaitableEvent::ResetPolicy::AUTOMATIC, - WaitableEvent::InitialState::NOT_SIGNALED); - WaitableEvent task_stop_event(WaitableEvent::ResetPolicy::AUTOMATIC, - WaitableEvent::InitialState::NOT_SIGNALED); - thread.task_runner()->PostTask( - FROM_HERE, Bind(&BlockUntilStopped, &task_start_event, &task_stop_event)); - task_start_event.Wait(); - - // The thread will timeout in this flush. - EndTraceAndFlushInThreadWithMessageLoop(); - Clear(); - - // Let the thread's message loop continue to spin. - task_stop_event.Signal(); - - // The following sequence ensures that the FlushCurrentThread task has been - // executed in the thread before continuing. - task_start_event.Reset(); - task_stop_event.Reset(); - thread.task_runner()->PostTask( - FROM_HERE, Bind(&BlockUntilStopped, &task_start_event, &task_stop_event)); - task_start_event.Wait(); - task_stop_event.Signal(); - Clear(); - - // TraceLog should discover the generation mismatch and recover the thread - // local buffer for the thread without any error. - BeginTrace(); - thread.task_runner()->PostTask( - FROM_HERE, Bind(&TraceWithAllMacroVariants, &task_complete_event)); - task_complete_event.Wait(); - task_complete_event.Reset(); - EndTraceAndFlushInThreadWithMessageLoop(); - ValidateAllTraceMacrosCreatedData(trace_parsed_); -} - -std::string* g_log_buffer = NULL; -bool MockLogMessageHandler(int, const char*, int, size_t, - const std::string& str) { - if (!g_log_buffer) - g_log_buffer = new std::string(); - g_log_buffer->append(str); - return false; -} - -TEST_F(TraceEventTestFixture, EchoToConsole) { - logging::LogMessageHandlerFunction old_log_message_handler = - logging::GetLogMessageHandler(); - logging::SetLogMessageHandler(MockLogMessageHandler); - - TraceLog::GetInstance()->SetEnabled( - TraceConfig(kRecordAllCategoryFilter, ECHO_TO_CONSOLE), - TraceLog::RECORDING_MODE); - TRACE_EVENT_BEGIN0("a", "begin_end"); - { - TRACE_EVENT0("b", "duration"); - TRACE_EVENT0("b1", "duration1"); - } - TRACE_EVENT_INSTANT0("c", "instant", TRACE_EVENT_SCOPE_GLOBAL); - TRACE_EVENT_END0("a", "begin_end"); - - EXPECT_NE(std::string::npos, g_log_buffer->find("begin_end[a]\x1b")); - EXPECT_NE(std::string::npos, g_log_buffer->find("| duration[b]\x1b")); - EXPECT_NE(std::string::npos, g_log_buffer->find("| | duration1[b1]\x1b")); - EXPECT_NE(std::string::npos, g_log_buffer->find("| | duration1[b1] (")); - EXPECT_NE(std::string::npos, g_log_buffer->find("| duration[b] (")); - EXPECT_NE(std::string::npos, g_log_buffer->find("| instant[c]\x1b")); - EXPECT_NE(std::string::npos, g_log_buffer->find("begin_end[a] (")); - - EndTraceAndFlush(); - delete g_log_buffer; - logging::SetLogMessageHandler(old_log_message_handler); - g_log_buffer = NULL; -} - -bool LogMessageHandlerWithTraceEvent(int, const char*, int, size_t, - const std::string&) { - TRACE_EVENT0("log", "trace_event"); - return false; -} - -TEST_F(TraceEventTestFixture, EchoToConsoleTraceEventRecursion) { - logging::LogMessageHandlerFunction old_log_message_handler = - logging::GetLogMessageHandler(); - logging::SetLogMessageHandler(LogMessageHandlerWithTraceEvent); - - TraceLog::GetInstance()->SetEnabled( - TraceConfig(kRecordAllCategoryFilter, ECHO_TO_CONSOLE), - TraceLog::RECORDING_MODE); - { - // This should not cause deadlock or infinite recursion. - TRACE_EVENT0("b", "duration"); - } - - EndTraceAndFlush(); - logging::SetLogMessageHandler(old_log_message_handler); -} - -TEST_F(TraceEventTestFixture, TimeOffset) { - BeginTrace(); - // Let TraceLog timer start from 0. - TimeDelta time_offset = TimeTicks::Now() - TimeTicks(); - TraceLog::GetInstance()->SetTimeOffset(time_offset); - - { - TRACE_EVENT0("all", "duration1"); - TRACE_EVENT0("all", "duration2"); - } - TRACE_EVENT_BEGIN_WITH_ID_TID_AND_TIMESTAMP0( - "all", "with_timestamp", 0, 0, TimeTicks::Now()); - TRACE_EVENT_END_WITH_ID_TID_AND_TIMESTAMP0( - "all", "with_timestamp", 0, 0, TimeTicks::Now()); - - EndTraceAndFlush(); - DropTracedMetadataRecords(); - - double end_time = static_cast<double>( - (TimeTicks::Now() - time_offset).ToInternalValue()); - double last_timestamp = 0; - for (size_t i = 0; i < trace_parsed_.GetSize(); ++i) { - const DictionaryValue* item; - EXPECT_TRUE(trace_parsed_.GetDictionary(i, &item)); - double timestamp; - EXPECT_TRUE(item->GetDouble("ts", ×tamp)); - EXPECT_GE(timestamp, last_timestamp); - EXPECT_LE(timestamp, end_time); - last_timestamp = timestamp; - } -} - -TEST_F(TraceEventTestFixture, ConfigureSyntheticDelays) { - BeginSpecificTrace("DELAY(test.Delay;0.05)"); - - base::TimeTicks start = base::TimeTicks::Now(); - { - TRACE_EVENT_SYNTHETIC_DELAY("test.Delay"); - } - base::TimeDelta duration = base::TimeTicks::Now() - start; - EXPECT_GE(duration.InMilliseconds(), 50); - - EndTraceAndFlush(); -} - -TEST_F(TraceEventTestFixture, BadSyntheticDelayConfigurations) { - const char* const filters[] = { - "", - "DELAY(", - "DELAY(;", - "DELAY(;)", - "DELAY(test.Delay)", - "DELAY(test.Delay;)" - }; - for (size_t i = 0; i < arraysize(filters); i++) { - BeginSpecificTrace(filters[i]); - EndTraceAndFlush(); - TraceConfig trace_config = TraceLog::GetInstance()->GetCurrentTraceConfig(); - EXPECT_EQ(0u, trace_config.GetSyntheticDelayValues().size()); - } -} - -TEST_F(TraceEventTestFixture, SyntheticDelayConfigurationMerging) { - TraceConfig config1("DELAY(test.Delay1;16)", ""); - TraceConfig config2("DELAY(test.Delay2;32)", ""); - config1.Merge(config2); - EXPECT_EQ(2u, config1.GetSyntheticDelayValues().size()); -} - -TEST_F(TraceEventTestFixture, SyntheticDelayConfigurationToString) { - const char filter[] = "DELAY(test.Delay;16;oneshot)"; - TraceConfig config(filter, ""); - EXPECT_EQ(filter, config.ToCategoryFilterString()); -} - -TEST_F(TraceEventTestFixture, TraceFilteringMode) { - const char config_json[] = - "{" - " \"event_filters\": [" - " {" - " \"filter_predicate\": \"testing_predicate\", " - " \"included_categories\": [\"*\"]" - " }" - " ]" - "}"; - - // Run RECORDING_MODE within FILTERING_MODE: - TestEventFilter::HitsCounter filter_hits_counter; - TestEventFilter::set_filter_return_value(true); - TraceLog::GetInstance()->SetFilterFactoryForTesting(TestEventFilter::Factory); - - // Only filtering mode is enabled with test filters. - TraceLog::GetInstance()->SetEnabled(TraceConfig(config_json), - TraceLog::FILTERING_MODE); - EXPECT_EQ(TraceLog::FILTERING_MODE, TraceLog::GetInstance()->enabled_modes()); - { - void* ptr = this; - TRACE_EVENT0("c0", "name0"); - TRACE_EVENT_ASYNC_BEGIN0("c1", "name1", ptr); - TRACE_EVENT_INSTANT0("c0", "name0", TRACE_EVENT_SCOPE_THREAD); - TRACE_EVENT_ASYNC_END0("c1", "name1", ptr); - } - - // Recording mode is enabled when filtering mode is turned on. - TraceLog::GetInstance()->SetEnabled(TraceConfig("", ""), - TraceLog::RECORDING_MODE); - EXPECT_EQ(TraceLog::RECORDING_MODE | TraceLog::FILTERING_MODE, - TraceLog::GetInstance()->enabled_modes()); - { - TRACE_EVENT0("c2", "name2"); - } - // Only recording mode is disabled and filtering mode will continue to run. - TraceLog::GetInstance()->SetDisabled(TraceLog::RECORDING_MODE); - EXPECT_EQ(TraceLog::FILTERING_MODE, TraceLog::GetInstance()->enabled_modes()); - - { - TRACE_EVENT0("c0", "name0"); - } - // Filtering mode is disabled and no tracing mode should be enabled. - TraceLog::GetInstance()->SetDisabled(TraceLog::FILTERING_MODE); - EXPECT_EQ(0, TraceLog::GetInstance()->enabled_modes()); - - EndTraceAndFlush(); - EXPECT_FALSE(FindMatchingValue("cat", "c0")); - EXPECT_FALSE(FindMatchingValue("cat", "c1")); - EXPECT_FALSE(FindMatchingValue("name", "name0")); - EXPECT_FALSE(FindMatchingValue("name", "name1")); - EXPECT_TRUE(FindMatchingValue("cat", "c2")); - EXPECT_TRUE(FindMatchingValue("name", "name2")); - EXPECT_EQ(6u, filter_hits_counter.filter_trace_event_hit_count); - EXPECT_EQ(3u, filter_hits_counter.end_event_hit_count); - Clear(); - filter_hits_counter.Reset(); - - // Run FILTERING_MODE within RECORDING_MODE: - // Only recording mode is enabled and all events must be recorded. - TraceLog::GetInstance()->SetEnabled(TraceConfig("", ""), - TraceLog::RECORDING_MODE); - EXPECT_EQ(TraceLog::RECORDING_MODE, TraceLog::GetInstance()->enabled_modes()); - { - TRACE_EVENT0("c0", "name0"); - } - - // Filtering mode is also enabled and all events must be filtered-out. - TestEventFilter::set_filter_return_value(false); - TraceLog::GetInstance()->SetEnabled(TraceConfig(config_json), - TraceLog::FILTERING_MODE); - EXPECT_EQ(TraceLog::RECORDING_MODE | TraceLog::FILTERING_MODE, - TraceLog::GetInstance()->enabled_modes()); - { - TRACE_EVENT0("c1", "name1"); - } - // Only filtering mode is disabled and recording mode should continue to run - // with all events being recorded. - TraceLog::GetInstance()->SetDisabled(TraceLog::FILTERING_MODE); - EXPECT_EQ(TraceLog::RECORDING_MODE, TraceLog::GetInstance()->enabled_modes()); - - { - TRACE_EVENT0("c2", "name2"); - } - // Recording mode is disabled and no tracing mode should be enabled. - TraceLog::GetInstance()->SetDisabled(TraceLog::RECORDING_MODE); - EXPECT_EQ(0, TraceLog::GetInstance()->enabled_modes()); - - EndTraceAndFlush(); - EXPECT_TRUE(FindMatchingValue("cat", "c0")); - EXPECT_TRUE(FindMatchingValue("cat", "c2")); - EXPECT_TRUE(FindMatchingValue("name", "name0")); - EXPECT_TRUE(FindMatchingValue("name", "name2")); - EXPECT_FALSE(FindMatchingValue("cat", "c1")); - EXPECT_FALSE(FindMatchingValue("name", "name1")); - EXPECT_EQ(1u, filter_hits_counter.filter_trace_event_hit_count); - EXPECT_EQ(1u, filter_hits_counter.end_event_hit_count); - Clear(); -} - -TEST_F(TraceEventTestFixture, EventFiltering) { - const char config_json[] = - "{" - " \"included_categories\": [" - " \"filtered_cat\"," - " \"unfiltered_cat\"," - " \"" TRACE_DISABLED_BY_DEFAULT("filtered_cat") "\"," - " \"" TRACE_DISABLED_BY_DEFAULT("unfiltered_cat") "\"]," - " \"event_filters\": [" - " {" - " \"filter_predicate\": \"testing_predicate\", " - " \"included_categories\": [" - " \"filtered_cat\"," - " \"" TRACE_DISABLED_BY_DEFAULT("filtered_cat") "\"]" - " }" - " " - " ]" - "}"; - - TestEventFilter::HitsCounter filter_hits_counter; - TestEventFilter::set_filter_return_value(true); - TraceLog::GetInstance()->SetFilterFactoryForTesting(TestEventFilter::Factory); - - TraceConfig trace_config(config_json); - TraceLog::GetInstance()->SetEnabled( - trace_config, TraceLog::RECORDING_MODE | TraceLog::FILTERING_MODE); - ASSERT_TRUE(TraceLog::GetInstance()->IsEnabled()); - - TRACE_EVENT0("filtered_cat", "a snake"); - TRACE_EVENT0("filtered_cat", "a mushroom"); - TRACE_EVENT0("unfiltered_cat", "a horse"); - - TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("filtered_cat"), "a dog"); - TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("unfiltered_cat"), "a pony"); - - // This is scoped so we can test the end event being filtered. - { TRACE_EVENT0("filtered_cat", "another cat whoa"); } - - EndTraceAndFlush(); - - EXPECT_EQ(4u, filter_hits_counter.filter_trace_event_hit_count); - EXPECT_EQ(1u, filter_hits_counter.end_event_hit_count); -} - -TEST_F(TraceEventTestFixture, EventWhitelistFiltering) { - std::string config_json = StringPrintf( - "{" - " \"included_categories\": [" - " \"filtered_cat\"," - " \"unfiltered_cat\"," - " \"" TRACE_DISABLED_BY_DEFAULT("filtered_cat") "\"]," - " \"event_filters\": [" - " {" - " \"filter_predicate\": \"%s\", " - " \"included_categories\": [" - " \"filtered_cat\"," - " \"" TRACE_DISABLED_BY_DEFAULT("*") "\"], " - " \"filter_args\": {" - " \"event_name_whitelist\": [\"a snake\", \"a dog\"]" - " }" - " }" - " " - " ]" - "}", - EventNameFilter::kName); - - TraceConfig trace_config(config_json); - TraceLog::GetInstance()->SetEnabled( - trace_config, TraceLog::RECORDING_MODE | TraceLog::FILTERING_MODE); - EXPECT_TRUE(TraceLog::GetInstance()->IsEnabled()); - - TRACE_EVENT0("filtered_cat", "a snake"); - TRACE_EVENT0("filtered_cat", "a mushroom"); - TRACE_EVENT0("unfiltered_cat", "a cat"); - TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("filtered_cat"), "a dog"); - TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("filtered_cat"), "a pony"); - - EndTraceAndFlush(); - - EXPECT_TRUE(FindMatchingValue("name", "a snake")); - EXPECT_FALSE(FindMatchingValue("name", "a mushroom")); - EXPECT_TRUE(FindMatchingValue("name", "a cat")); - EXPECT_TRUE(FindMatchingValue("name", "a dog")); - EXPECT_FALSE(FindMatchingValue("name", "a pony")); -} - -TEST_F(TraceEventTestFixture, HeapProfilerFiltering) { - std::string config_json = StringPrintf( - "{" - " \"included_categories\": [" - " \"filtered_cat\"," - " \"unfiltered_cat\"," - " \"" TRACE_DISABLED_BY_DEFAULT("filtered_cat") "\"," - " \"" TRACE_DISABLED_BY_DEFAULT("unfiltered_cat") "\"]," - " \"excluded_categories\": [\"excluded_cat\"]," - " \"event_filters\": [" - " {" - " \"filter_predicate\": \"%s\", " - " \"included_categories\": [" - " \"*\"," - " \"" TRACE_DISABLED_BY_DEFAULT("filtered_cat") "\"]" - " }" - " ]" - "}", - HeapProfilerEventFilter::kName); - - TraceConfig trace_config(config_json); - TraceLog::GetInstance()->SetEnabled( - trace_config, TraceLog::RECORDING_MODE | TraceLog::FILTERING_MODE); - EXPECT_TRUE(TraceLog::GetInstance()->IsEnabled()); - - TRACE_EVENT0("filtered_cat", "a snake"); - TRACE_EVENT0("excluded_cat", "a mushroom"); - TRACE_EVENT0("unfiltered_cat", "a cat"); - TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("filtered_cat"), "a dog"); - TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("unfiltered_cat"), "a pony"); - - EndTraceAndFlush(); - - // The predicate should not change behavior of the trace events. - EXPECT_TRUE(FindMatchingValue("name", "a snake")); - EXPECT_FALSE(FindMatchingValue("name", "a mushroom")); - EXPECT_TRUE(FindMatchingValue("name", "a cat")); - EXPECT_TRUE(FindMatchingValue("name", "a dog")); - EXPECT_TRUE(FindMatchingValue("name", "a pony")); -} - -TEST_F(TraceEventTestFixture, ClockSyncEventsAreAlwaysAddedToTrace) { - BeginSpecificTrace("-*"); - TRACE_EVENT_CLOCK_SYNC_RECEIVER(1); - EndTraceAndFlush(); - EXPECT_TRUE(FindNamePhase("clock_sync", "c")); -} - -} // namespace trace_event -} // namespace base diff --git a/base/trace_event/trace_log.cc b/base/trace_event/trace_log.cc deleted file mode 100644 index d08030e709..0000000000 --- a/base/trace_event/trace_log.cc +++ /dev/null @@ -1,1749 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/trace_event/trace_log.h" - -#include <algorithm> -#include <cmath> -#include <memory> -#include <utility> - -#include "base/base_switches.h" -#include "base/bind.h" -#include "base/command_line.h" -#include "base/debug/leak_annotations.h" -#include "base/location.h" -#include "base/macros.h" -#include "base/memory/ptr_util.h" -#include "base/memory/ref_counted_memory.h" -#include "base/memory/singleton.h" -#include "base/message_loop/message_loop.h" -#include "base/process/process_info.h" -#include "base/process/process_metrics.h" -#include "base/stl_util.h" -#include "base/strings/string_piece.h" -#include "base/strings/string_split.h" -#include "base/strings/string_tokenizer.h" -#include "base/strings/stringprintf.h" -#include "base/sys_info.h" -// post_task.h pulls in a lot of code not needed on Arc++. -#if 0 -#include "base/task_scheduler/post_task.h" -#endif -#include "base/threading/platform_thread.h" -#include "base/threading/thread_id_name_manager.h" -#include "base/threading/thread_task_runner_handle.h" -#include "base/time/time.h" -#include "base/trace_event/category_registry.h" -#include "base/trace_event/event_name_filter.h" -#include "base/trace_event/heap_profiler.h" -#include "base/trace_event/heap_profiler_allocation_context_tracker.h" -#include "base/trace_event/heap_profiler_event_filter.h" -#include "base/trace_event/memory_dump_manager.h" -#include "base/trace_event/memory_dump_provider.h" -#include "base/trace_event/process_memory_dump.h" -#include "base/trace_event/trace_buffer.h" -#include "base/trace_event/trace_event.h" -#include "base/trace_event/trace_event_synthetic_delay.h" -#include "build/build_config.h" - -#if defined(OS_WIN) -#include "base/trace_event/trace_event_etw_export_win.h" -#endif - -namespace base { -namespace internal { - -class DeleteTraceLogForTesting { - public: - static void Delete() { - Singleton<trace_event::TraceLog, - LeakySingletonTraits<trace_event::TraceLog>>::OnExit(0); - } -}; - -} // namespace internal - -namespace trace_event { - -namespace { - -// Controls the number of trace events we will buffer in-memory -// before throwing them away. -const size_t kTraceBufferChunkSize = TraceBufferChunk::kTraceBufferChunkSize; - -const size_t kTraceEventVectorBigBufferChunks = - 512000000 / kTraceBufferChunkSize; -static_assert( - kTraceEventVectorBigBufferChunks <= TraceBufferChunk::kMaxChunkIndex, - "Too many big buffer chunks"); -const size_t kTraceEventVectorBufferChunks = 256000 / kTraceBufferChunkSize; -static_assert( - kTraceEventVectorBufferChunks <= TraceBufferChunk::kMaxChunkIndex, - "Too many vector buffer chunks"); -const size_t kTraceEventRingBufferChunks = kTraceEventVectorBufferChunks / 4; - -// ECHO_TO_CONSOLE needs a small buffer to hold the unfinished COMPLETE events. -const size_t kEchoToConsoleTraceEventBufferChunks = 256; - -const size_t kTraceEventBufferSizeInBytes = 100 * 1024; -const int kThreadFlushTimeoutMs = 3000; - -#define MAX_TRACE_EVENT_FILTERS 32 - -// List of TraceEventFilter objects from the most recent tracing session. -std::vector<std::unique_ptr<TraceEventFilter>>& GetCategoryGroupFilters() { - static auto* filters = new std::vector<std::unique_ptr<TraceEventFilter>>(); - return *filters; -} - -ThreadTicks ThreadNow() { - return ThreadTicks::IsSupported() ? ThreadTicks::Now() : ThreadTicks(); -} - -template <typename T> -void InitializeMetadataEvent(TraceEvent* trace_event, - int thread_id, - const char* metadata_name, - const char* arg_name, - const T& value) { - if (!trace_event) - return; - - int num_args = 1; - unsigned char arg_type; - unsigned long long arg_value; - ::trace_event_internal::SetTraceValue(value, &arg_type, &arg_value); - trace_event->Initialize( - thread_id, - TimeTicks(), - ThreadTicks(), - TRACE_EVENT_PHASE_METADATA, - CategoryRegistry::kCategoryMetadata->state_ptr(), - metadata_name, - trace_event_internal::kGlobalScope, // scope - trace_event_internal::kNoId, // id - trace_event_internal::kNoId, // bind_id - num_args, - &arg_name, - &arg_type, - &arg_value, - nullptr, - TRACE_EVENT_FLAG_NONE); -} - -class AutoThreadLocalBoolean { - public: - explicit AutoThreadLocalBoolean(ThreadLocalBoolean* thread_local_boolean) - : thread_local_boolean_(thread_local_boolean) { - DCHECK(!thread_local_boolean_->Get()); - thread_local_boolean_->Set(true); - } - ~AutoThreadLocalBoolean() { thread_local_boolean_->Set(false); } - - private: - ThreadLocalBoolean* thread_local_boolean_; - DISALLOW_COPY_AND_ASSIGN(AutoThreadLocalBoolean); -}; - -// Use this function instead of TraceEventHandle constructor to keep the -// overhead of ScopedTracer (trace_event.h) constructor minimum. -void MakeHandle(uint32_t chunk_seq, - size_t chunk_index, - size_t event_index, - TraceEventHandle* handle) { - DCHECK(chunk_seq); - DCHECK(chunk_index <= TraceBufferChunk::kMaxChunkIndex); - DCHECK(event_index < TraceBufferChunk::kTraceBufferChunkSize); - DCHECK(chunk_index <= std::numeric_limits<uint16_t>::max()); - handle->chunk_seq = chunk_seq; - handle->chunk_index = static_cast<uint16_t>(chunk_index); - handle->event_index = static_cast<uint16_t>(event_index); -} - -template <typename Function> -void ForEachCategoryFilter(const unsigned char* category_group_enabled, - Function filter_fn) { - const TraceCategory* category = - CategoryRegistry::GetCategoryByStatePtr(category_group_enabled); - uint32_t filter_bitmap = category->enabled_filters(); - for (int index = 0; filter_bitmap != 0; filter_bitmap >>= 1, index++) { - if (filter_bitmap & 1 && GetCategoryGroupFilters()[index]) - filter_fn(GetCategoryGroupFilters()[index].get()); - } -} - -} // namespace - -// A helper class that allows the lock to be acquired in the middle of the scope -// and unlocks at the end of scope if locked. -class TraceLog::OptionalAutoLock { - public: - explicit OptionalAutoLock(Lock* lock) : lock_(lock), locked_(false) {} - - ~OptionalAutoLock() { - if (locked_) - lock_->Release(); - } - - void EnsureAcquired() { - if (!locked_) { - lock_->Acquire(); - locked_ = true; - } - } - - private: - Lock* lock_; - bool locked_; - DISALLOW_COPY_AND_ASSIGN(OptionalAutoLock); -}; - -class TraceLog::ThreadLocalEventBuffer - : public MessageLoop::DestructionObserver, - public MemoryDumpProvider { - public: - explicit ThreadLocalEventBuffer(TraceLog* trace_log); - ~ThreadLocalEventBuffer() override; - - TraceEvent* AddTraceEvent(TraceEventHandle* handle); - - TraceEvent* GetEventByHandle(TraceEventHandle handle) { - if (!chunk_ || handle.chunk_seq != chunk_->seq() || - handle.chunk_index != chunk_index_) { - return nullptr; - } - - return chunk_->GetEventAt(handle.event_index); - } - - int generation() const { return generation_; } - - private: - // MessageLoop::DestructionObserver - void WillDestroyCurrentMessageLoop() override; - - // MemoryDumpProvider implementation. - bool OnMemoryDump(const MemoryDumpArgs& args, - ProcessMemoryDump* pmd) override; - - void FlushWhileLocked(); - - void CheckThisIsCurrentBuffer() const { - DCHECK(trace_log_->thread_local_event_buffer_.Get() == this); - } - - // Since TraceLog is a leaky singleton, trace_log_ will always be valid - // as long as the thread exists. - TraceLog* trace_log_; - std::unique_ptr<TraceBufferChunk> chunk_; - size_t chunk_index_; - int generation_; - - DISALLOW_COPY_AND_ASSIGN(ThreadLocalEventBuffer); -}; - -TraceLog::ThreadLocalEventBuffer::ThreadLocalEventBuffer(TraceLog* trace_log) - : trace_log_(trace_log), - chunk_index_(0), - generation_(trace_log->generation()) { - // ThreadLocalEventBuffer is created only if the thread has a message loop, so - // the following message_loop won't be NULL. - MessageLoop* message_loop = MessageLoop::current(); - message_loop->AddDestructionObserver(this); - - // This is to report the local memory usage when memory-infra is enabled. - MemoryDumpManager::GetInstance()->RegisterDumpProvider( - this, "ThreadLocalEventBuffer", ThreadTaskRunnerHandle::Get()); - - AutoLock lock(trace_log->lock_); - trace_log->thread_message_loops_.insert(message_loop); -} - -TraceLog::ThreadLocalEventBuffer::~ThreadLocalEventBuffer() { - CheckThisIsCurrentBuffer(); - MessageLoop::current()->RemoveDestructionObserver(this); - MemoryDumpManager::GetInstance()->UnregisterDumpProvider(this); - - { - AutoLock lock(trace_log_->lock_); - FlushWhileLocked(); - trace_log_->thread_message_loops_.erase(MessageLoop::current()); - } - trace_log_->thread_local_event_buffer_.Set(NULL); -} - -TraceEvent* TraceLog::ThreadLocalEventBuffer::AddTraceEvent( - TraceEventHandle* handle) { - CheckThisIsCurrentBuffer(); - - if (chunk_ && chunk_->IsFull()) { - AutoLock lock(trace_log_->lock_); - FlushWhileLocked(); - chunk_.reset(); - } - if (!chunk_) { - AutoLock lock(trace_log_->lock_); - chunk_ = trace_log_->logged_events_->GetChunk(&chunk_index_); - trace_log_->CheckIfBufferIsFullWhileLocked(); - } - if (!chunk_) - return NULL; - - size_t event_index; - TraceEvent* trace_event = chunk_->AddTraceEvent(&event_index); - if (trace_event && handle) - MakeHandle(chunk_->seq(), chunk_index_, event_index, handle); - - return trace_event; -} - -void TraceLog::ThreadLocalEventBuffer::WillDestroyCurrentMessageLoop() { - delete this; -} - -bool TraceLog::ThreadLocalEventBuffer::OnMemoryDump(const MemoryDumpArgs& args, - ProcessMemoryDump* pmd) { - if (!chunk_) - return true; - std::string dump_base_name = StringPrintf( - "tracing/thread_%d", static_cast<int>(PlatformThread::CurrentId())); - TraceEventMemoryOverhead overhead; - chunk_->EstimateTraceMemoryOverhead(&overhead); - overhead.DumpInto(dump_base_name.c_str(), pmd); - return true; -} - -void TraceLog::ThreadLocalEventBuffer::FlushWhileLocked() { - if (!chunk_) - return; - - trace_log_->lock_.AssertAcquired(); - if (trace_log_->CheckGeneration(generation_)) { - // Return the chunk to the buffer only if the generation matches. - trace_log_->logged_events_->ReturnChunk(chunk_index_, std::move(chunk_)); - } - // Otherwise this method may be called from the destructor, or TraceLog will - // find the generation mismatch and delete this buffer soon. -} - -struct TraceLog::RegisteredAsyncObserver { - explicit RegisteredAsyncObserver(WeakPtr<AsyncEnabledStateObserver> observer) - : observer(observer), task_runner(ThreadTaskRunnerHandle::Get()) {} - ~RegisteredAsyncObserver() {} - - WeakPtr<AsyncEnabledStateObserver> observer; - scoped_refptr<SequencedTaskRunner> task_runner; -}; - -TraceLogStatus::TraceLogStatus() : event_capacity(0), event_count(0) {} - -TraceLogStatus::~TraceLogStatus() {} - -// static -TraceLog* TraceLog::GetInstance() { - return Singleton<TraceLog, LeakySingletonTraits<TraceLog>>::get(); -} - -TraceLog::TraceLog() - : enabled_modes_(0), - num_traces_recorded_(0), - dispatching_to_observer_list_(false), - process_sort_index_(0), - process_id_hash_(0), - process_id_(0), - trace_options_(kInternalRecordUntilFull), - trace_config_(TraceConfig()), - thread_shared_chunk_index_(0), - generation_(0), - use_worker_thread_(false), - filter_factory_for_testing_(nullptr) { - CategoryRegistry::Initialize(); - -#if defined(OS_NACL) // NaCl shouldn't expose the process id. - SetProcessID(0); -#else - SetProcessID(static_cast<int>(GetCurrentProcId())); -#endif - - logged_events_.reset(CreateTraceBuffer()); - - MemoryDumpManager::GetInstance()->RegisterDumpProvider(this, "TraceLog", - nullptr); -} - -TraceLog::~TraceLog() {} - -void TraceLog::InitializeThreadLocalEventBufferIfSupported() { - // A ThreadLocalEventBuffer needs the message loop - // - to know when the thread exits; - // - to handle the final flush. - // For a thread without a message loop or the message loop may be blocked, the - // trace events will be added into the main buffer directly. - if (thread_blocks_message_loop_.Get() || !MessageLoop::current()) - return; - HEAP_PROFILER_SCOPED_IGNORE; - auto* thread_local_event_buffer = thread_local_event_buffer_.Get(); - if (thread_local_event_buffer && - !CheckGeneration(thread_local_event_buffer->generation())) { - delete thread_local_event_buffer; - thread_local_event_buffer = NULL; - } - if (!thread_local_event_buffer) { - thread_local_event_buffer = new ThreadLocalEventBuffer(this); - thread_local_event_buffer_.Set(thread_local_event_buffer); - } -} - -bool TraceLog::OnMemoryDump(const MemoryDumpArgs& args, - ProcessMemoryDump* pmd) { - // TODO(ssid): Use MemoryDumpArgs to create light dumps when requested - // (crbug.com/499731). - TraceEventMemoryOverhead overhead; - overhead.Add("TraceLog", sizeof(*this)); - { - AutoLock lock(lock_); - if (logged_events_) - logged_events_->EstimateTraceMemoryOverhead(&overhead); - - for (auto& metadata_event : metadata_events_) - metadata_event->EstimateTraceMemoryOverhead(&overhead); - } - overhead.AddSelf(); - overhead.DumpInto("tracing/main_trace_log", pmd); - return true; -} - -const unsigned char* TraceLog::GetCategoryGroupEnabled( - const char* category_group) { - TraceLog* tracelog = GetInstance(); - if (!tracelog) { - DCHECK(!CategoryRegistry::kCategoryAlreadyShutdown->is_enabled()); - return CategoryRegistry::kCategoryAlreadyShutdown->state_ptr(); - } - TraceCategory* category = CategoryRegistry::GetCategoryByName(category_group); - if (!category) { - // Slow path: in the case of a new category we have to repeat the check - // holding the lock, as multiple threads might have reached this point - // at the same time. - auto category_initializer = [](TraceCategory* category) { - TraceLog::GetInstance()->UpdateCategoryState(category); - }; - AutoLock lock(tracelog->lock_); - CategoryRegistry::GetOrCreateCategoryLocked( - category_group, category_initializer, &category); - } - DCHECK(category->state_ptr()); - return category->state_ptr(); -} - -const char* TraceLog::GetCategoryGroupName( - const unsigned char* category_group_enabled) { - return CategoryRegistry::GetCategoryByStatePtr(category_group_enabled) - ->name(); -} - -void TraceLog::UpdateCategoryState(TraceCategory* category) { - lock_.AssertAcquired(); - DCHECK(category->is_valid()); - unsigned char state_flags = 0; - if (enabled_modes_ & RECORDING_MODE && - trace_config_.IsCategoryGroupEnabled(category->name())) { - state_flags |= TraceCategory::ENABLED_FOR_RECORDING; - } - - // TODO(primiano): this is a temporary workaround for catapult:#2341, - // to guarantee that metadata events are always added even if the category - // filter is "-*". See crbug.com/618054 for more details and long-term fix. - if (enabled_modes_ & RECORDING_MODE && - category == CategoryRegistry::kCategoryMetadata) { - state_flags |= TraceCategory::ENABLED_FOR_RECORDING; - } - -#if defined(OS_WIN) - if (base::trace_event::TraceEventETWExport::IsCategoryGroupEnabled( - category->name())) { - state_flags |= TraceCategory::ENABLED_FOR_ETW_EXPORT; - } -#endif - - uint32_t enabled_filters_bitmap = 0; - int index = 0; - for (const auto& event_filter : enabled_event_filters_) { - if (event_filter.IsCategoryGroupEnabled(category->name())) { - state_flags |= TraceCategory::ENABLED_FOR_FILTERING; - DCHECK(GetCategoryGroupFilters()[index]); - enabled_filters_bitmap |= 1 << index; - } - if (index++ >= MAX_TRACE_EVENT_FILTERS) { - NOTREACHED(); - break; - } - } - category->set_enabled_filters(enabled_filters_bitmap); - category->set_state(state_flags); -} - -void TraceLog::UpdateCategoryRegistry() { - lock_.AssertAcquired(); - CreateFiltersForTraceConfig(); - for (TraceCategory& category : CategoryRegistry::GetAllCategories()) { - UpdateCategoryState(&category); - } -} - -void TraceLog::CreateFiltersForTraceConfig() { - if (!(enabled_modes_ & FILTERING_MODE)) - return; - - // Filters were already added and tracing could be enabled. Filters list - // cannot be changed when trace events are using them. - if (GetCategoryGroupFilters().size()) - return; - - for (auto& filter_config : enabled_event_filters_) { - if (GetCategoryGroupFilters().size() >= MAX_TRACE_EVENT_FILTERS) { - NOTREACHED() - << "Too many trace event filters installed in the current session"; - break; - } - - std::unique_ptr<TraceEventFilter> new_filter; - const std::string& predicate_name = filter_config.predicate_name(); - if (predicate_name == EventNameFilter::kName) { - auto whitelist = MakeUnique<std::unordered_set<std::string>>(); - CHECK(filter_config.GetArgAsSet("event_name_whitelist", &*whitelist)); - new_filter = MakeUnique<EventNameFilter>(std::move(whitelist)); - } else if (predicate_name == HeapProfilerEventFilter::kName) { - new_filter = MakeUnique<HeapProfilerEventFilter>(); - } else { - if (filter_factory_for_testing_) - new_filter = filter_factory_for_testing_(predicate_name); - CHECK(new_filter) << "Unknown trace filter " << predicate_name; - } - GetCategoryGroupFilters().push_back(std::move(new_filter)); - } -} - -void TraceLog::UpdateSyntheticDelaysFromTraceConfig() { - ResetTraceEventSyntheticDelays(); - const TraceConfig::StringList& delays = - trace_config_.GetSyntheticDelayValues(); - TraceConfig::StringList::const_iterator ci; - for (ci = delays.begin(); ci != delays.end(); ++ci) { - StringTokenizer tokens(*ci, ";"); - if (!tokens.GetNext()) - continue; - TraceEventSyntheticDelay* delay = - TraceEventSyntheticDelay::Lookup(tokens.token()); - while (tokens.GetNext()) { - std::string token = tokens.token(); - char* duration_end; - double target_duration = strtod(token.c_str(), &duration_end); - if (duration_end != token.c_str()) { - delay->SetTargetDuration(TimeDelta::FromMicroseconds( - static_cast<int64_t>(target_duration * 1e6))); - } else if (token == "static") { - delay->SetMode(TraceEventSyntheticDelay::STATIC); - } else if (token == "oneshot") { - delay->SetMode(TraceEventSyntheticDelay::ONE_SHOT); - } else if (token == "alternating") { - delay->SetMode(TraceEventSyntheticDelay::ALTERNATING); - } - } - } -} - -void TraceLog::GetKnownCategoryGroups( - std::vector<std::string>* category_groups) { - for (const auto& category : CategoryRegistry::GetAllCategories()) { - if (!CategoryRegistry::IsBuiltinCategory(&category)) - category_groups->push_back(category.name()); - } -} - -void TraceLog::SetEnabled(const TraceConfig& trace_config, - uint8_t modes_to_enable) { - std::vector<EnabledStateObserver*> observer_list; - std::map<AsyncEnabledStateObserver*, RegisteredAsyncObserver> observer_map; - { - AutoLock lock(lock_); - - // Can't enable tracing when Flush() is in progress. - DCHECK(!flush_task_runner_); - - InternalTraceOptions new_options = - GetInternalOptionsFromTraceConfig(trace_config); - - InternalTraceOptions old_options = trace_options(); - - if (dispatching_to_observer_list_) { - // TODO(ssid): Change to NOTREACHED after fixing crbug.com/625170. - DLOG(ERROR) - << "Cannot manipulate TraceLog::Enabled state from an observer."; - return; - } - - // Clear all filters from previous tracing session. These filters are not - // cleared at the end of tracing because some threads which hit trace event - // when disabling, could try to use the filters. - if (!enabled_modes_) - GetCategoryGroupFilters().clear(); - - // Update trace config for recording. - const bool already_recording = enabled_modes_ & RECORDING_MODE; - if (modes_to_enable & RECORDING_MODE) { - if (already_recording) { - // TODO(ssid): Stop suporting enabling of RECODING_MODE when already - // enabled crbug.com/625170. - DCHECK_EQ(new_options, old_options) << "Attempting to re-enable " - "tracing with a different set " - "of options."; - trace_config_.Merge(trace_config); - } else { - trace_config_ = trace_config; - } - } - - // Update event filters. - if (modes_to_enable & FILTERING_MODE) { - DCHECK(!trace_config.event_filters().empty()) - << "Attempting to enable filtering without any filters"; - DCHECK(enabled_event_filters_.empty()) << "Attempting to re-enable " - "filtering when filters are " - "already enabled."; - - // Use the given event filters only if filtering was not enabled. - if (enabled_event_filters_.empty()) - enabled_event_filters_ = trace_config.event_filters(); - } - // Keep the |trace_config_| updated with only enabled filters in case anyone - // tries to read it using |GetCurrentTraceConfig| (even if filters are - // empty). - trace_config_.SetEventFilters(enabled_event_filters_); - - enabled_modes_ |= modes_to_enable; - UpdateCategoryRegistry(); - - // Do not notify observers or create trace buffer if only enabled for - // filtering or if recording was already enabled. - if (!(modes_to_enable & RECORDING_MODE) || already_recording) - return; - - if (new_options != old_options) { - subtle::NoBarrier_Store(&trace_options_, new_options); - UseNextTraceBuffer(); - } - - num_traces_recorded_++; - - UpdateCategoryRegistry(); - UpdateSyntheticDelaysFromTraceConfig(); - - dispatching_to_observer_list_ = true; - observer_list = enabled_state_observer_list_; - observer_map = async_observers_; - } - // Notify observers outside the lock in case they trigger trace events. - for (EnabledStateObserver* observer : observer_list) - observer->OnTraceLogEnabled(); - for (const auto& it : observer_map) { - it.second.task_runner->PostTask( - FROM_HERE, Bind(&AsyncEnabledStateObserver::OnTraceLogEnabled, - it.second.observer)); - } - - { - AutoLock lock(lock_); - dispatching_to_observer_list_ = false; - } -} - -void TraceLog::SetArgumentFilterPredicate( - const ArgumentFilterPredicate& argument_filter_predicate) { - AutoLock lock(lock_); - DCHECK(!argument_filter_predicate.is_null()); - DCHECK(argument_filter_predicate_.is_null()); - argument_filter_predicate_ = argument_filter_predicate; -} - -TraceLog::InternalTraceOptions TraceLog::GetInternalOptionsFromTraceConfig( - const TraceConfig& config) { - InternalTraceOptions ret = config.IsArgumentFilterEnabled() - ? kInternalEnableArgumentFilter - : kInternalNone; - switch (config.GetTraceRecordMode()) { - case RECORD_UNTIL_FULL: - return ret | kInternalRecordUntilFull; - case RECORD_CONTINUOUSLY: - return ret | kInternalRecordContinuously; - case ECHO_TO_CONSOLE: - return ret | kInternalEchoToConsole; - case RECORD_AS_MUCH_AS_POSSIBLE: - return ret | kInternalRecordAsMuchAsPossible; - } - NOTREACHED(); - return kInternalNone; -} - -TraceConfig TraceLog::GetCurrentTraceConfig() const { - AutoLock lock(lock_); - return trace_config_; -} - -void TraceLog::SetDisabled() { - AutoLock lock(lock_); - SetDisabledWhileLocked(RECORDING_MODE); -} - -void TraceLog::SetDisabled(uint8_t modes_to_disable) { - AutoLock lock(lock_); - SetDisabledWhileLocked(modes_to_disable); -} - -void TraceLog::SetDisabledWhileLocked(uint8_t modes_to_disable) { - lock_.AssertAcquired(); - - if (!(enabled_modes_ & modes_to_disable)) - return; - - if (dispatching_to_observer_list_) { - // TODO(ssid): Change to NOTREACHED after fixing crbug.com/625170. - DLOG(ERROR) - << "Cannot manipulate TraceLog::Enabled state from an observer."; - return; - } - - bool is_recording_mode_disabled = - (enabled_modes_ & RECORDING_MODE) && (modes_to_disable & RECORDING_MODE); - enabled_modes_ &= ~modes_to_disable; - - if (modes_to_disable & FILTERING_MODE) - enabled_event_filters_.clear(); - - if (modes_to_disable & RECORDING_MODE) - trace_config_.Clear(); - - UpdateCategoryRegistry(); - - // Add metadata events and notify observers only if recording mode was - // disabled now. - if (!is_recording_mode_disabled) - return; - - AddMetadataEventsWhileLocked(); - - // Remove metadata events so they will not get added to a subsequent trace. - metadata_events_.clear(); - - dispatching_to_observer_list_ = true; - std::vector<EnabledStateObserver*> observer_list = - enabled_state_observer_list_; - std::map<AsyncEnabledStateObserver*, RegisteredAsyncObserver> observer_map = - async_observers_; - - { - // Dispatch to observers outside the lock in case the observer triggers a - // trace event. - AutoUnlock unlock(lock_); - for (EnabledStateObserver* observer : observer_list) - observer->OnTraceLogDisabled(); - for (const auto& it : observer_map) { - it.second.task_runner->PostTask( - FROM_HERE, Bind(&AsyncEnabledStateObserver::OnTraceLogDisabled, - it.second.observer)); - } - } - dispatching_to_observer_list_ = false; -} - -int TraceLog::GetNumTracesRecorded() { - AutoLock lock(lock_); - if (!IsEnabled()) - return -1; - return num_traces_recorded_; -} - -void TraceLog::AddEnabledStateObserver(EnabledStateObserver* listener) { - AutoLock lock(lock_); - enabled_state_observer_list_.push_back(listener); -} - -void TraceLog::RemoveEnabledStateObserver(EnabledStateObserver* listener) { - AutoLock lock(lock_); - std::vector<EnabledStateObserver*>::iterator it = - std::find(enabled_state_observer_list_.begin(), - enabled_state_observer_list_.end(), listener); - if (it != enabled_state_observer_list_.end()) - enabled_state_observer_list_.erase(it); -} - -bool TraceLog::HasEnabledStateObserver(EnabledStateObserver* listener) const { - AutoLock lock(lock_); - return ContainsValue(enabled_state_observer_list_, listener); -} - -TraceLogStatus TraceLog::GetStatus() const { - AutoLock lock(lock_); - TraceLogStatus result; - result.event_capacity = static_cast<uint32_t>(logged_events_->Capacity()); - result.event_count = static_cast<uint32_t>(logged_events_->Size()); - return result; -} - -bool TraceLog::BufferIsFull() const { - AutoLock lock(lock_); - return logged_events_->IsFull(); -} - -TraceEvent* TraceLog::AddEventToThreadSharedChunkWhileLocked( - TraceEventHandle* handle, - bool check_buffer_is_full) { - lock_.AssertAcquired(); - - if (thread_shared_chunk_ && thread_shared_chunk_->IsFull()) { - logged_events_->ReturnChunk(thread_shared_chunk_index_, - std::move(thread_shared_chunk_)); - } - - if (!thread_shared_chunk_) { - thread_shared_chunk_ = - logged_events_->GetChunk(&thread_shared_chunk_index_); - if (check_buffer_is_full) - CheckIfBufferIsFullWhileLocked(); - } - if (!thread_shared_chunk_) - return NULL; - - size_t event_index; - TraceEvent* trace_event = thread_shared_chunk_->AddTraceEvent(&event_index); - if (trace_event && handle) { - MakeHandle(thread_shared_chunk_->seq(), thread_shared_chunk_index_, - event_index, handle); - } - return trace_event; -} - -void TraceLog::CheckIfBufferIsFullWhileLocked() { - lock_.AssertAcquired(); - if (logged_events_->IsFull()) { - if (buffer_limit_reached_timestamp_.is_null()) { - buffer_limit_reached_timestamp_ = OffsetNow(); - } - SetDisabledWhileLocked(RECORDING_MODE); - } -} - -// Flush() works as the following: -// 1. Flush() is called in thread A whose task runner is saved in -// flush_task_runner_; -// 2. If thread_message_loops_ is not empty, thread A posts task to each message -// loop to flush the thread local buffers; otherwise finish the flush; -// 3. FlushCurrentThread() deletes the thread local event buffer: -// - The last batch of events of the thread are flushed into the main buffer; -// - The message loop will be removed from thread_message_loops_; -// If this is the last message loop, finish the flush; -// 4. If any thread hasn't finish its flush in time, finish the flush. -void TraceLog::Flush(const TraceLog::OutputCallback& cb, - bool use_worker_thread) { - FlushInternal(cb, use_worker_thread, false); -} - -void TraceLog::CancelTracing(const OutputCallback& cb) { - SetDisabled(); - FlushInternal(cb, false, true); -} - -void TraceLog::FlushInternal(const TraceLog::OutputCallback& cb, - bool use_worker_thread, - bool discard_events) { - use_worker_thread_ = use_worker_thread; - if (IsEnabled()) { - // Can't flush when tracing is enabled because otherwise PostTask would - // - generate more trace events; - // - deschedule the calling thread on some platforms causing inaccurate - // timing of the trace events. - scoped_refptr<RefCountedString> empty_result = new RefCountedString; - if (!cb.is_null()) - cb.Run(empty_result, false); - LOG(WARNING) << "Ignored TraceLog::Flush called when tracing is enabled"; - return; - } - - int gen = generation(); - // Copy of thread_message_loops_ to be used without locking. - std::vector<scoped_refptr<SingleThreadTaskRunner>> - thread_message_loop_task_runners; - { - AutoLock lock(lock_); - DCHECK(!flush_task_runner_); - flush_task_runner_ = ThreadTaskRunnerHandle::IsSet() - ? ThreadTaskRunnerHandle::Get() - : nullptr; - DCHECK(thread_message_loops_.empty() || flush_task_runner_); - flush_output_callback_ = cb; - - if (thread_shared_chunk_) { - logged_events_->ReturnChunk(thread_shared_chunk_index_, - std::move(thread_shared_chunk_)); - } - - for (MessageLoop* loop : thread_message_loops_) - thread_message_loop_task_runners.push_back(loop->task_runner()); - } - - if (!thread_message_loop_task_runners.empty()) { - for (auto& task_runner : thread_message_loop_task_runners) { - task_runner->PostTask( - FROM_HERE, Bind(&TraceLog::FlushCurrentThread, Unretained(this), - gen, discard_events)); - } - flush_task_runner_->PostDelayedTask( - FROM_HERE, Bind(&TraceLog::OnFlushTimeout, Unretained(this), gen, - discard_events), - TimeDelta::FromMilliseconds(kThreadFlushTimeoutMs)); - return; - } - - FinishFlush(gen, discard_events); -} - -// Usually it runs on a different thread. -void TraceLog::ConvertTraceEventsToTraceFormat( - std::unique_ptr<TraceBuffer> logged_events, - const OutputCallback& flush_output_callback, - const ArgumentFilterPredicate& argument_filter_predicate) { - if (flush_output_callback.is_null()) - return; - - HEAP_PROFILER_SCOPED_IGNORE; - // The callback need to be called at least once even if there is no events - // to let the caller know the completion of flush. - scoped_refptr<RefCountedString> json_events_str_ptr = new RefCountedString(); - while (const TraceBufferChunk* chunk = logged_events->NextChunk()) { - for (size_t j = 0; j < chunk->size(); ++j) { - size_t size = json_events_str_ptr->size(); - if (size > kTraceEventBufferSizeInBytes) { - flush_output_callback.Run(json_events_str_ptr, true); - json_events_str_ptr = new RefCountedString(); - } else if (size) { - json_events_str_ptr->data().append(",\n"); - } - chunk->GetEventAt(j)->AppendAsJSON(&(json_events_str_ptr->data()), - argument_filter_predicate); - } - } - flush_output_callback.Run(json_events_str_ptr, false); -} - -void TraceLog::FinishFlush(int generation, bool discard_events) { - std::unique_ptr<TraceBuffer> previous_logged_events; - OutputCallback flush_output_callback; - ArgumentFilterPredicate argument_filter_predicate; - - if (!CheckGeneration(generation)) - return; - - { - AutoLock lock(lock_); - - previous_logged_events.swap(logged_events_); - UseNextTraceBuffer(); - thread_message_loops_.clear(); - - flush_task_runner_ = NULL; - flush_output_callback = flush_output_callback_; - flush_output_callback_.Reset(); - - if (trace_options() & kInternalEnableArgumentFilter) { - CHECK(!argument_filter_predicate_.is_null()); - argument_filter_predicate = argument_filter_predicate_; - } - } - - if (discard_events) { - if (!flush_output_callback.is_null()) { - scoped_refptr<RefCountedString> empty_result = new RefCountedString; - flush_output_callback.Run(empty_result, false); - } - return; - } - - if (use_worker_thread_) { -#if 0 - base::PostTaskWithTraits( - FROM_HERE, base::TaskTraits() - .MayBlock() - .WithPriority(base::TaskPriority::BACKGROUND) - .WithShutdownBehavior( - base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN), - Bind(&TraceLog::ConvertTraceEventsToTraceFormat, - Passed(&previous_logged_events), flush_output_callback, - argument_filter_predicate)); - return; -#else - NOTREACHED(); -#endif - } - - ConvertTraceEventsToTraceFormat(std::move(previous_logged_events), - flush_output_callback, - argument_filter_predicate); -} - -// Run in each thread holding a local event buffer. -void TraceLog::FlushCurrentThread(int generation, bool discard_events) { - { - AutoLock lock(lock_); - if (!CheckGeneration(generation) || !flush_task_runner_) { - // This is late. The corresponding flush has finished. - return; - } - } - - // This will flush the thread local buffer. - delete thread_local_event_buffer_.Get(); - - AutoLock lock(lock_); - if (!CheckGeneration(generation) || !flush_task_runner_ || - !thread_message_loops_.empty()) - return; - - flush_task_runner_->PostTask( - FROM_HERE, Bind(&TraceLog::FinishFlush, Unretained(this), generation, - discard_events)); -} - -void TraceLog::OnFlushTimeout(int generation, bool discard_events) { - { - AutoLock lock(lock_); - if (!CheckGeneration(generation) || !flush_task_runner_) { - // Flush has finished before timeout. - return; - } - - LOG(WARNING) - << "The following threads haven't finished flush in time. " - "If this happens stably for some thread, please call " - "TraceLog::GetInstance()->SetCurrentThreadBlocksMessageLoop() from " - "the thread to avoid its trace events from being lost."; - for (hash_set<MessageLoop*>::const_iterator it = - thread_message_loops_.begin(); - it != thread_message_loops_.end(); ++it) { - LOG(WARNING) << "Thread: " << (*it)->GetThreadName(); - } - } - FinishFlush(generation, discard_events); -} - -void TraceLog::UseNextTraceBuffer() { - logged_events_.reset(CreateTraceBuffer()); - subtle::NoBarrier_AtomicIncrement(&generation_, 1); - thread_shared_chunk_.reset(); - thread_shared_chunk_index_ = 0; -} - -TraceEventHandle TraceLog::AddTraceEvent( - char phase, - const unsigned char* category_group_enabled, - const char* name, - const char* scope, - unsigned long long id, - int num_args, - const char** arg_names, - const unsigned char* arg_types, - const unsigned long long* arg_values, - std::unique_ptr<ConvertableToTraceFormat>* convertable_values, - unsigned int flags) { - int thread_id = static_cast<int>(base::PlatformThread::CurrentId()); - base::TimeTicks now = base::TimeTicks::Now(); - return AddTraceEventWithThreadIdAndTimestamp( - phase, - category_group_enabled, - name, - scope, - id, - trace_event_internal::kNoId, // bind_id - thread_id, - now, - num_args, - arg_names, - arg_types, - arg_values, - convertable_values, - flags); -} - -TraceEventHandle TraceLog::AddTraceEventWithBindId( - char phase, - const unsigned char* category_group_enabled, - const char* name, - const char* scope, - unsigned long long id, - unsigned long long bind_id, - int num_args, - const char** arg_names, - const unsigned char* arg_types, - const unsigned long long* arg_values, - std::unique_ptr<ConvertableToTraceFormat>* convertable_values, - unsigned int flags) { - int thread_id = static_cast<int>(base::PlatformThread::CurrentId()); - base::TimeTicks now = base::TimeTicks::Now(); - return AddTraceEventWithThreadIdAndTimestamp( - phase, - category_group_enabled, - name, - scope, - id, - bind_id, - thread_id, - now, - num_args, - arg_names, - arg_types, - arg_values, - convertable_values, - flags | TRACE_EVENT_FLAG_HAS_CONTEXT_ID); -} - -TraceEventHandle TraceLog::AddTraceEventWithProcessId( - char phase, - const unsigned char* category_group_enabled, - const char* name, - const char* scope, - unsigned long long id, - int process_id, - int num_args, - const char** arg_names, - const unsigned char* arg_types, - const unsigned long long* arg_values, - std::unique_ptr<ConvertableToTraceFormat>* convertable_values, - unsigned int flags) { - base::TimeTicks now = base::TimeTicks::Now(); - return AddTraceEventWithThreadIdAndTimestamp( - phase, - category_group_enabled, - name, - scope, - id, - trace_event_internal::kNoId, // bind_id - process_id, - now, - num_args, - arg_names, - arg_types, - arg_values, - convertable_values, - flags | TRACE_EVENT_FLAG_HAS_PROCESS_ID); -} - -// Handle legacy calls to AddTraceEventWithThreadIdAndTimestamp -// with kNoId as bind_id -TraceEventHandle TraceLog::AddTraceEventWithThreadIdAndTimestamp( - char phase, - const unsigned char* category_group_enabled, - const char* name, - const char* scope, - unsigned long long id, - int thread_id, - const TimeTicks& timestamp, - int num_args, - const char** arg_names, - const unsigned char* arg_types, - const unsigned long long* arg_values, - std::unique_ptr<ConvertableToTraceFormat>* convertable_values, - unsigned int flags) { - return AddTraceEventWithThreadIdAndTimestamp( - phase, - category_group_enabled, - name, - scope, - id, - trace_event_internal::kNoId, // bind_id - thread_id, - timestamp, - num_args, - arg_names, - arg_types, - arg_values, - convertable_values, - flags); -} - -TraceEventHandle TraceLog::AddTraceEventWithThreadIdAndTimestamp( - char phase, - const unsigned char* category_group_enabled, - const char* name, - const char* scope, - unsigned long long id, - unsigned long long bind_id, - int thread_id, - const TimeTicks& timestamp, - int num_args, - const char** arg_names, - const unsigned char* arg_types, - const unsigned long long* arg_values, - std::unique_ptr<ConvertableToTraceFormat>* convertable_values, - unsigned int flags) { - TraceEventHandle handle = {0, 0, 0}; - if (!*category_group_enabled) - return handle; - - // Avoid re-entrance of AddTraceEvent. This may happen in GPU process when - // ECHO_TO_CONSOLE is enabled: AddTraceEvent -> LOG(ERROR) -> - // GpuProcessLogMessageHandler -> PostPendingTask -> TRACE_EVENT ... - if (thread_is_in_trace_event_.Get()) - return handle; - - AutoThreadLocalBoolean thread_is_in_trace_event(&thread_is_in_trace_event_); - - DCHECK(name); - DCHECK(!timestamp.is_null()); - - if (flags & TRACE_EVENT_FLAG_MANGLE_ID) { - if ((flags & TRACE_EVENT_FLAG_FLOW_IN) || - (flags & TRACE_EVENT_FLAG_FLOW_OUT)) - bind_id = MangleEventId(bind_id); - id = MangleEventId(id); - } - - TimeTicks offset_event_timestamp = OffsetTimestamp(timestamp); - ThreadTicks thread_now = ThreadNow(); - - ThreadLocalEventBuffer* thread_local_event_buffer = nullptr; - if (*category_group_enabled & RECORDING_MODE) { - // |thread_local_event_buffer_| can be null if the current thread doesn't - // have a message loop or the message loop is blocked. - InitializeThreadLocalEventBufferIfSupported(); - thread_local_event_buffer = thread_local_event_buffer_.Get(); - } - - // Check and update the current thread name only if the event is for the - // current thread to avoid locks in most cases. - if (thread_id == static_cast<int>(PlatformThread::CurrentId())) { - const char* new_name = - ThreadIdNameManager::GetInstance()->GetName(thread_id); - // Check if the thread name has been set or changed since the previous - // call (if any), but don't bother if the new name is empty. Note this will - // not detect a thread name change within the same char* buffer address: we - // favor common case performance over corner case correctness. - static auto* current_thread_name = new ThreadLocalPointer<const char>(); - if (new_name != current_thread_name->Get() && new_name && *new_name) { - current_thread_name->Set(new_name); - - AutoLock thread_info_lock(thread_info_lock_); - - hash_map<int, std::string>::iterator existing_name = - thread_names_.find(thread_id); - if (existing_name == thread_names_.end()) { - // This is a new thread id, and a new name. - thread_names_[thread_id] = new_name; - } else { - // This is a thread id that we've seen before, but potentially with a - // new name. - std::vector<StringPiece> existing_names = base::SplitStringPiece( - existing_name->second, ",", base::KEEP_WHITESPACE, - base::SPLIT_WANT_NONEMPTY); - bool found = std::find(existing_names.begin(), existing_names.end(), - new_name) != existing_names.end(); - if (!found) { - if (!existing_names.empty()) - existing_name->second.push_back(','); - existing_name->second.append(new_name); - } - } - } - } - -#if defined(OS_WIN) - // This is done sooner rather than later, to avoid creating the event and - // acquiring the lock, which is not needed for ETW as it's already threadsafe. - if (*category_group_enabled & TraceCategory::ENABLED_FOR_ETW_EXPORT) - TraceEventETWExport::AddEvent(phase, category_group_enabled, name, id, - num_args, arg_names, arg_types, arg_values, - convertable_values); -#endif // OS_WIN - - std::string console_message; - std::unique_ptr<TraceEvent> filtered_trace_event; - bool disabled_by_filters = false; - if (*category_group_enabled & TraceCategory::ENABLED_FOR_FILTERING) { - std::unique_ptr<TraceEvent> new_trace_event(new TraceEvent); - new_trace_event->Initialize(thread_id, offset_event_timestamp, thread_now, - phase, category_group_enabled, name, scope, id, - bind_id, num_args, arg_names, arg_types, - arg_values, convertable_values, flags); - - disabled_by_filters = true; - ForEachCategoryFilter( - category_group_enabled, [&new_trace_event, &disabled_by_filters]( - TraceEventFilter* trace_event_filter) { - if (trace_event_filter->FilterTraceEvent(*new_trace_event)) - disabled_by_filters = false; - }); - if (!disabled_by_filters) - filtered_trace_event = std::move(new_trace_event); - } - - // If enabled for recording, the event should be added only if one of the - // filters indicates or category is not enabled for filtering. - if ((*category_group_enabled & TraceCategory::ENABLED_FOR_RECORDING) && - !disabled_by_filters) { - OptionalAutoLock lock(&lock_); - - TraceEvent* trace_event = NULL; - if (thread_local_event_buffer) { - trace_event = thread_local_event_buffer->AddTraceEvent(&handle); - } else { - lock.EnsureAcquired(); - trace_event = AddEventToThreadSharedChunkWhileLocked(&handle, true); - } - - if (trace_event) { - if (filtered_trace_event) { - trace_event->MoveFrom(std::move(filtered_trace_event)); - } else { - trace_event->Initialize(thread_id, offset_event_timestamp, thread_now, - phase, category_group_enabled, name, scope, id, - bind_id, num_args, arg_names, arg_types, - arg_values, convertable_values, flags); - } - -#if defined(OS_ANDROID) - trace_event->SendToATrace(); -#endif - } - - if (trace_options() & kInternalEchoToConsole) { - console_message = EventToConsoleMessage( - phase == TRACE_EVENT_PHASE_COMPLETE ? TRACE_EVENT_PHASE_BEGIN : phase, - timestamp, trace_event); - } - } - - if (!console_message.empty()) - LOG(ERROR) << console_message; - - return handle; -} - -void TraceLog::AddMetadataEvent( - const unsigned char* category_group_enabled, - const char* name, - int num_args, - const char** arg_names, - const unsigned char* arg_types, - const unsigned long long* arg_values, - std::unique_ptr<ConvertableToTraceFormat>* convertable_values, - unsigned int flags) { - HEAP_PROFILER_SCOPED_IGNORE; - std::unique_ptr<TraceEvent> trace_event(new TraceEvent); - int thread_id = static_cast<int>(base::PlatformThread::CurrentId()); - ThreadTicks thread_now = ThreadNow(); - TimeTicks now = OffsetNow(); - AutoLock lock(lock_); - trace_event->Initialize( - thread_id, now, thread_now, TRACE_EVENT_PHASE_METADATA, - category_group_enabled, name, - trace_event_internal::kGlobalScope, // scope - trace_event_internal::kNoId, // id - trace_event_internal::kNoId, // bind_id - num_args, arg_names, arg_types, arg_values, convertable_values, flags); - metadata_events_.push_back(std::move(trace_event)); -} - -// May be called when a COMPELETE event ends and the unfinished event has been -// recycled (phase == TRACE_EVENT_PHASE_END and trace_event == NULL). -std::string TraceLog::EventToConsoleMessage(unsigned char phase, - const TimeTicks& timestamp, - TraceEvent* trace_event) { - HEAP_PROFILER_SCOPED_IGNORE; - AutoLock thread_info_lock(thread_info_lock_); - - // The caller should translate TRACE_EVENT_PHASE_COMPLETE to - // TRACE_EVENT_PHASE_BEGIN or TRACE_EVENT_END. - DCHECK(phase != TRACE_EVENT_PHASE_COMPLETE); - - TimeDelta duration; - int thread_id = - trace_event ? trace_event->thread_id() : PlatformThread::CurrentId(); - if (phase == TRACE_EVENT_PHASE_END) { - duration = timestamp - thread_event_start_times_[thread_id].top(); - thread_event_start_times_[thread_id].pop(); - } - - std::string thread_name = thread_names_[thread_id]; - if (thread_colors_.find(thread_name) == thread_colors_.end()) - thread_colors_[thread_name] = (thread_colors_.size() % 6) + 1; - - std::ostringstream log; - log << base::StringPrintf("%s: \x1b[0;3%dm", thread_name.c_str(), - thread_colors_[thread_name]); - - size_t depth = 0; - auto it = thread_event_start_times_.find(thread_id); - if (it != thread_event_start_times_.end()) - depth = it->second.size(); - - for (size_t i = 0; i < depth; ++i) - log << "| "; - - if (trace_event) - trace_event->AppendPrettyPrinted(&log); - if (phase == TRACE_EVENT_PHASE_END) - log << base::StringPrintf(" (%.3f ms)", duration.InMillisecondsF()); - - log << "\x1b[0;m"; - - if (phase == TRACE_EVENT_PHASE_BEGIN) - thread_event_start_times_[thread_id].push(timestamp); - - return log.str(); -} - -void TraceLog::EndFilteredEvent(const unsigned char* category_group_enabled, - const char* name, - TraceEventHandle handle) { - const char* category_name = GetCategoryGroupName(category_group_enabled); - ForEachCategoryFilter( - category_group_enabled, - [name, category_name](TraceEventFilter* trace_event_filter) { - trace_event_filter->EndEvent(category_name, name); - }); -} - -void TraceLog::UpdateTraceEventDuration( - const unsigned char* category_group_enabled, - const char* name, - TraceEventHandle handle) { - char category_group_enabled_local = *category_group_enabled; - if (!category_group_enabled_local) - return; - - // Avoid re-entrance of AddTraceEvent. This may happen in GPU process when - // ECHO_TO_CONSOLE is enabled: AddTraceEvent -> LOG(ERROR) -> - // GpuProcessLogMessageHandler -> PostPendingTask -> TRACE_EVENT ... - if (thread_is_in_trace_event_.Get()) - return; - - AutoThreadLocalBoolean thread_is_in_trace_event(&thread_is_in_trace_event_); - - ThreadTicks thread_now = ThreadNow(); - TimeTicks now = OffsetNow(); - -#if defined(OS_WIN) - // Generate an ETW event that marks the end of a complete event. - if (category_group_enabled_local & TraceCategory::ENABLED_FOR_ETW_EXPORT) - TraceEventETWExport::AddCompleteEndEvent(name); -#endif // OS_WIN - - std::string console_message; - if (category_group_enabled_local & TraceCategory::ENABLED_FOR_RECORDING) { - OptionalAutoLock lock(&lock_); - - TraceEvent* trace_event = GetEventByHandleInternal(handle, &lock); - if (trace_event) { - DCHECK(trace_event->phase() == TRACE_EVENT_PHASE_COMPLETE); - // TEMP(oysteine) to debug crbug.com/638744 - if (trace_event->duration().ToInternalValue() != -1) { - DVLOG(1) << "TraceHandle: chunk_seq " << handle.chunk_seq - << ", chunk_index " << handle.chunk_index << ", event_index " - << handle.event_index; - - std::string serialized_event; - trace_event->AppendAsJSON(&serialized_event, ArgumentFilterPredicate()); - DVLOG(1) << "TraceEvent: " << serialized_event; - lock_.AssertAcquired(); - } - - trace_event->UpdateDuration(now, thread_now); -#if defined(OS_ANDROID) - trace_event->SendToATrace(); -#endif - } - - if (trace_options() & kInternalEchoToConsole) { - console_message = - EventToConsoleMessage(TRACE_EVENT_PHASE_END, now, trace_event); - } - } - - if (!console_message.empty()) - LOG(ERROR) << console_message; - - if (category_group_enabled_local & TraceCategory::ENABLED_FOR_FILTERING) - EndFilteredEvent(category_group_enabled, name, handle); -} - -uint64_t TraceLog::MangleEventId(uint64_t id) { - return id ^ process_id_hash_; -} - -void TraceLog::AddMetadataEventsWhileLocked() { - lock_.AssertAcquired(); - - // Move metadata added by |AddMetadataEvent| into the trace log. - while (!metadata_events_.empty()) { - TraceEvent* event = AddEventToThreadSharedChunkWhileLocked(nullptr, false); - event->MoveFrom(std::move(metadata_events_.back())); - metadata_events_.pop_back(); - } - -#if !defined(OS_NACL) // NaCl shouldn't expose the process id. - InitializeMetadataEvent(AddEventToThreadSharedChunkWhileLocked(NULL, false), - 0, "num_cpus", "number", - base::SysInfo::NumberOfProcessors()); -#endif - - int current_thread_id = static_cast<int>(base::PlatformThread::CurrentId()); - if (process_sort_index_ != 0) { - InitializeMetadataEvent(AddEventToThreadSharedChunkWhileLocked(NULL, false), - current_thread_id, "process_sort_index", - "sort_index", process_sort_index_); - } - - if (!process_name_.empty()) { - InitializeMetadataEvent(AddEventToThreadSharedChunkWhileLocked(NULL, false), - current_thread_id, "process_name", "name", - process_name_); - } - -#if !defined(OS_NACL) && !defined(OS_IOS) - Time process_creation_time = CurrentProcessInfo::CreationTime(); - if (!process_creation_time.is_null()) { - TimeDelta process_uptime = Time::Now() - process_creation_time; - InitializeMetadataEvent(AddEventToThreadSharedChunkWhileLocked(NULL, false), - current_thread_id, "process_uptime_seconds", - "uptime", process_uptime.InSeconds()); - } -#endif // !defined(OS_NACL) && !defined(OS_IOS) - - if (!process_labels_.empty()) { - std::vector<base::StringPiece> labels; - for (const auto& it : process_labels_) - labels.push_back(it.second); - InitializeMetadataEvent(AddEventToThreadSharedChunkWhileLocked(NULL, false), - current_thread_id, "process_labels", "labels", - base::JoinString(labels, ",")); - } - - // Thread sort indices. - for (const auto& it : thread_sort_indices_) { - if (it.second == 0) - continue; - InitializeMetadataEvent(AddEventToThreadSharedChunkWhileLocked(NULL, false), - it.first, "thread_sort_index", "sort_index", - it.second); - } - - // Thread names. - AutoLock thread_info_lock(thread_info_lock_); - for (const auto& it : thread_names_) { - if (it.second.empty()) - continue; - InitializeMetadataEvent(AddEventToThreadSharedChunkWhileLocked(NULL, false), - it.first, "thread_name", "name", it.second); - } - - // If buffer is full, add a metadata record to report this. - if (!buffer_limit_reached_timestamp_.is_null()) { - InitializeMetadataEvent(AddEventToThreadSharedChunkWhileLocked(NULL, false), - current_thread_id, "trace_buffer_overflowed", - "overflowed_at_ts", - buffer_limit_reached_timestamp_); - } -} - -void TraceLog::DeleteForTesting() { - internal::DeleteTraceLogForTesting::Delete(); - CategoryRegistry::ResetForTesting(); -} - -TraceEvent* TraceLog::GetEventByHandle(TraceEventHandle handle) { - return GetEventByHandleInternal(handle, NULL); -} - -TraceEvent* TraceLog::GetEventByHandleInternal(TraceEventHandle handle, - OptionalAutoLock* lock) { - if (!handle.chunk_seq) - return NULL; - - DCHECK(handle.chunk_seq); - DCHECK(handle.chunk_index <= TraceBufferChunk::kMaxChunkIndex); - DCHECK(handle.event_index < TraceBufferChunk::kTraceBufferChunkSize); - - if (thread_local_event_buffer_.Get()) { - TraceEvent* trace_event = - thread_local_event_buffer_.Get()->GetEventByHandle(handle); - if (trace_event) - return trace_event; - } - - // The event has been out-of-control of the thread local buffer. - // Try to get the event from the main buffer with a lock. - if (lock) - lock->EnsureAcquired(); - - if (thread_shared_chunk_ && - handle.chunk_index == thread_shared_chunk_index_) { - return handle.chunk_seq == thread_shared_chunk_->seq() - ? thread_shared_chunk_->GetEventAt(handle.event_index) - : NULL; - } - - return logged_events_->GetEventByHandle(handle); -} - -void TraceLog::SetProcessID(int process_id) { - process_id_ = process_id; - // Create a FNV hash from the process ID for XORing. - // See http://isthe.com/chongo/tech/comp/fnv/ for algorithm details. - const unsigned long long kOffsetBasis = 14695981039346656037ull; - const unsigned long long kFnvPrime = 1099511628211ull; - const unsigned long long pid = static_cast<unsigned long long>(process_id_); - process_id_hash_ = (kOffsetBasis ^ pid) * kFnvPrime; -} - -void TraceLog::SetProcessSortIndex(int sort_index) { - AutoLock lock(lock_); - process_sort_index_ = sort_index; -} - -void TraceLog::SetProcessName(const char* process_name) { - AutoLock lock(lock_); - process_name_ = process_name; -} - -void TraceLog::UpdateProcessLabel(int label_id, - const std::string& current_label) { - if (!current_label.length()) - return RemoveProcessLabel(label_id); - - AutoLock lock(lock_); - process_labels_[label_id] = current_label; -} - -void TraceLog::RemoveProcessLabel(int label_id) { - AutoLock lock(lock_); - process_labels_.erase(label_id); -} - -void TraceLog::SetThreadSortIndex(PlatformThreadId thread_id, int sort_index) { - AutoLock lock(lock_); - thread_sort_indices_[static_cast<int>(thread_id)] = sort_index; -} - -void TraceLog::SetTimeOffset(TimeDelta offset) { - time_offset_ = offset; -} - -size_t TraceLog::GetObserverCountForTest() const { - return enabled_state_observer_list_.size(); -} - -void TraceLog::SetCurrentThreadBlocksMessageLoop() { - thread_blocks_message_loop_.Set(true); - // This will flush the thread local buffer. - delete thread_local_event_buffer_.Get(); -} - -TraceBuffer* TraceLog::CreateTraceBuffer() { - HEAP_PROFILER_SCOPED_IGNORE; - InternalTraceOptions options = trace_options(); - if (options & kInternalRecordContinuously) { - return TraceBuffer::CreateTraceBufferRingBuffer( - kTraceEventRingBufferChunks); - } - if (options & kInternalEchoToConsole) { - return TraceBuffer::CreateTraceBufferRingBuffer( - kEchoToConsoleTraceEventBufferChunks); - } - if (options & kInternalRecordAsMuchAsPossible) { - return TraceBuffer::CreateTraceBufferVectorOfSize( - kTraceEventVectorBigBufferChunks); - } - return TraceBuffer::CreateTraceBufferVectorOfSize( - kTraceEventVectorBufferChunks); -} - -#if defined(OS_WIN) -void TraceLog::UpdateETWCategoryGroupEnabledFlags() { - // Go through each category and set/clear the ETW bit depending on whether the - // category is enabled. - for (TraceCategory& category : CategoryRegistry::GetAllCategories()) { - if (base::trace_event::TraceEventETWExport::IsCategoryGroupEnabled( - category.name())) { - category.set_state_flag(TraceCategory::ENABLED_FOR_ETW_EXPORT); - } else { - category.clear_state_flag(TraceCategory::ENABLED_FOR_ETW_EXPORT); - } - } -} -#endif // defined(OS_WIN) - -void ConvertableToTraceFormat::EstimateTraceMemoryOverhead( - TraceEventMemoryOverhead* overhead) { - overhead->Add("ConvertableToTraceFormat(Unknown)", sizeof(*this)); -} - -void TraceLog::AddAsyncEnabledStateObserver( - WeakPtr<AsyncEnabledStateObserver> listener) { - AutoLock lock(lock_); - async_observers_.insert( - std::make_pair(listener.get(), RegisteredAsyncObserver(listener))); -} - -void TraceLog::RemoveAsyncEnabledStateObserver( - AsyncEnabledStateObserver* listener) { - AutoLock lock(lock_); - async_observers_.erase(listener); -} - -bool TraceLog::HasAsyncEnabledStateObserver( - AsyncEnabledStateObserver* listener) const { - AutoLock lock(lock_); - return ContainsKey(async_observers_, listener); -} - -} // namespace trace_event -} // namespace base - -namespace trace_event_internal { - -ScopedTraceBinaryEfficient::ScopedTraceBinaryEfficient( - const char* category_group, - const char* name) { - // The single atom works because for now the category_group can only be "gpu". - DCHECK_EQ(strcmp(category_group, "gpu"), 0); - static TRACE_EVENT_API_ATOMIC_WORD atomic = 0; - INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO_CUSTOM_VARIABLES( - category_group, atomic, category_group_enabled_); - name_ = name; - if (*category_group_enabled_) { - event_handle_ = - TRACE_EVENT_API_ADD_TRACE_EVENT_WITH_THREAD_ID_AND_TIMESTAMP( - TRACE_EVENT_PHASE_COMPLETE, - category_group_enabled_, - name, - trace_event_internal::kGlobalScope, // scope - trace_event_internal::kNoId, // id - static_cast<int>(base::PlatformThread::CurrentId()), // thread_id - base::TimeTicks::Now(), - trace_event_internal::kZeroNumArgs, - nullptr, - nullptr, - nullptr, - nullptr, - TRACE_EVENT_FLAG_NONE); - } -} - -ScopedTraceBinaryEfficient::~ScopedTraceBinaryEfficient() { - if (*category_group_enabled_) { - TRACE_EVENT_API_UPDATE_TRACE_EVENT_DURATION(category_group_enabled_, name_, - event_handle_); - } -} - -} // namespace trace_event_internal diff --git a/base/trace_event/trace_log.h b/base/trace_event/trace_log.h deleted file mode 100644 index 88b6e588e4..0000000000 --- a/base/trace_event/trace_log.h +++ /dev/null @@ -1,507 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_TRACE_EVENT_TRACE_LOG_H_ -#define BASE_TRACE_EVENT_TRACE_LOG_H_ - -#include <stddef.h> -#include <stdint.h> - -#include <memory> -#include <string> -#include <vector> - -#include "base/atomicops.h" -#include "base/containers/hash_tables.h" -#include "base/gtest_prod_util.h" -#include "base/macros.h" -#include "base/memory/scoped_vector.h" -#include "base/trace_event/memory_dump_provider.h" -#include "base/trace_event/trace_config.h" -#include "base/trace_event/trace_event_impl.h" -#include "build/build_config.h" - -namespace base { - -template <typename Type> -struct DefaultSingletonTraits; -class MessageLoop; -class RefCountedString; - -namespace trace_event { - -struct TraceCategory; -class TraceBuffer; -class TraceBufferChunk; -class TraceEvent; -class TraceEventFilter; -class TraceEventMemoryOverhead; - -struct BASE_EXPORT TraceLogStatus { - TraceLogStatus(); - ~TraceLogStatus(); - uint32_t event_capacity; - uint32_t event_count; -}; - -class BASE_EXPORT TraceLog : public MemoryDumpProvider { - public: - // Argument passed to TraceLog::SetEnabled. - enum Mode : uint8_t { - // Enables normal tracing (recording trace events in the trace buffer). - RECORDING_MODE = 1 << 0, - - // Trace events are enabled just for filtering but not for recording. Only - // event filters config of |trace_config| argument is used. - FILTERING_MODE = 1 << 1 - }; - - static TraceLog* GetInstance(); - - // Get set of known category groups. This can change as new code paths are - // reached. The known category groups are inserted into |category_groups|. - void GetKnownCategoryGroups(std::vector<std::string>* category_groups); - - // Retrieves a copy (for thread-safety) of the current TraceConfig. - TraceConfig GetCurrentTraceConfig() const; - - // Initializes the thread-local event buffer, if not already initialized and - // if the current thread supports that (has a message loop). - void InitializeThreadLocalEventBufferIfSupported(); - - // See TraceConfig comments for details on how to control which categories - // will be traced. SetDisabled must be called distinctly for each mode that is - // enabled. If tracing has already been enabled for recording, category filter - // (enabled and disabled categories) will be merged into the current category - // filter. Enabling RECORDING_MODE does not enable filters. Trace event - // filters will be used only if FILTERING_MODE is set on |modes_to_enable|. - // Conversely to RECORDING_MODE, FILTERING_MODE doesn't support upgrading, - // i.e. filters can only be enabled if not previously enabled. - void SetEnabled(const TraceConfig& trace_config, uint8_t modes_to_enable); - - // TODO(ssid): Remove the default SetEnabled and IsEnabled. They should take - // Mode as argument. - - // Disables tracing for all categories for the specified |modes_to_disable| - // only. Only RECORDING_MODE is taken as default |modes_to_disable|. - void SetDisabled(); - void SetDisabled(uint8_t modes_to_disable); - - // Returns true if TraceLog is enabled on recording mode. - // Note: Returns false even if FILTERING_MODE is enabled. - bool IsEnabled() { return enabled_modes_ & RECORDING_MODE; } - - // Returns a bitmap of enabled modes from TraceLog::Mode. - uint8_t enabled_modes() { return enabled_modes_; } - - // The number of times we have begun recording traces. If tracing is off, - // returns -1. If tracing is on, then it returns the number of times we have - // recorded a trace. By watching for this number to increment, you can - // passively discover when a new trace has begun. This is then used to - // implement the TRACE_EVENT_IS_NEW_TRACE() primitive. - int GetNumTracesRecorded(); - -#if defined(OS_ANDROID) - void StartATrace(); - void StopATrace(); - void AddClockSyncMetadataEvent(); -#endif - - // Enabled state listeners give a callback when tracing is enabled or - // disabled. This can be used to tie into other library's tracing systems - // on-demand. - class BASE_EXPORT EnabledStateObserver { - public: - virtual ~EnabledStateObserver() = default; - - // Called just after the tracing system becomes enabled, outside of the - // |lock_|. TraceLog::IsEnabled() is true at this point. - virtual void OnTraceLogEnabled() = 0; - - // Called just after the tracing system disables, outside of the |lock_|. - // TraceLog::IsEnabled() is false at this point. - virtual void OnTraceLogDisabled() = 0; - }; - void AddEnabledStateObserver(EnabledStateObserver* listener); - void RemoveEnabledStateObserver(EnabledStateObserver* listener); - bool HasEnabledStateObserver(EnabledStateObserver* listener) const; - - // Asynchronous enabled state listeners. When tracing is enabled or disabled, - // for each observer, a task for invoking its appropriate callback is posted - // to the thread from which AddAsyncEnabledStateObserver() was called. This - // allows the observer to be safely destroyed, provided that it happens on the - // same thread that invoked AddAsyncEnabledStateObserver(). - class BASE_EXPORT AsyncEnabledStateObserver { - public: - virtual ~AsyncEnabledStateObserver() = default; - - // Posted just after the tracing system becomes enabled, outside |lock_|. - // TraceLog::IsEnabled() is true at this point. - virtual void OnTraceLogEnabled() = 0; - - // Posted just after the tracing system becomes disabled, outside |lock_|. - // TraceLog::IsEnabled() is false at this point. - virtual void OnTraceLogDisabled() = 0; - }; - void AddAsyncEnabledStateObserver( - WeakPtr<AsyncEnabledStateObserver> listener); - void RemoveAsyncEnabledStateObserver(AsyncEnabledStateObserver* listener); - bool HasAsyncEnabledStateObserver(AsyncEnabledStateObserver* listener) const; - - TraceLogStatus GetStatus() const; - bool BufferIsFull() const; - - // Computes an estimate of the size of the TraceLog including all the retained - // objects. - void EstimateTraceMemoryOverhead(TraceEventMemoryOverhead* overhead); - - void SetArgumentFilterPredicate( - const ArgumentFilterPredicate& argument_filter_predicate); - - // Flush all collected events to the given output callback. The callback will - // be called one or more times either synchronously or asynchronously from - // the current thread with IPC-bite-size chunks. The string format is - // undefined. Use TraceResultBuffer to convert one or more trace strings to - // JSON. The callback can be null if the caller doesn't want any data. - // Due to the implementation of thread-local buffers, flush can't be - // done when tracing is enabled. If called when tracing is enabled, the - // callback will be called directly with (empty_string, false) to indicate - // the end of this unsuccessful flush. Flush does the serialization - // on the same thread if the caller doesn't set use_worker_thread explicitly. - typedef base::Callback<void(const scoped_refptr<base::RefCountedString>&, - bool has_more_events)> OutputCallback; - void Flush(const OutputCallback& cb, bool use_worker_thread = false); - - // Cancels tracing and discards collected data. - void CancelTracing(const OutputCallback& cb); - - // Called by TRACE_EVENT* macros, don't call this directly. - // The name parameter is a category group for example: - // TRACE_EVENT0("renderer,webkit", "WebViewImpl::HandleInputEvent") - static const unsigned char* GetCategoryGroupEnabled(const char* name); - static const char* GetCategoryGroupName( - const unsigned char* category_group_enabled); - - // Called by TRACE_EVENT* macros, don't call this directly. - // If |copy| is set, |name|, |arg_name1| and |arg_name2| will be deep copied - // into the event; see "Memory scoping note" and TRACE_EVENT_COPY_XXX above. - TraceEventHandle AddTraceEvent( - char phase, - const unsigned char* category_group_enabled, - const char* name, - const char* scope, - unsigned long long id, - int num_args, - const char** arg_names, - const unsigned char* arg_types, - const unsigned long long* arg_values, - std::unique_ptr<ConvertableToTraceFormat>* convertable_values, - unsigned int flags); - TraceEventHandle AddTraceEventWithBindId( - char phase, - const unsigned char* category_group_enabled, - const char* name, - const char* scope, - unsigned long long id, - unsigned long long bind_id, - int num_args, - const char** arg_names, - const unsigned char* arg_types, - const unsigned long long* arg_values, - std::unique_ptr<ConvertableToTraceFormat>* convertable_values, - unsigned int flags); - TraceEventHandle AddTraceEventWithProcessId( - char phase, - const unsigned char* category_group_enabled, - const char* name, - const char* scope, - unsigned long long id, - int process_id, - int num_args, - const char** arg_names, - const unsigned char* arg_types, - const unsigned long long* arg_values, - std::unique_ptr<ConvertableToTraceFormat>* convertable_values, - unsigned int flags); - TraceEventHandle AddTraceEventWithThreadIdAndTimestamp( - char phase, - const unsigned char* category_group_enabled, - const char* name, - const char* scope, - unsigned long long id, - int thread_id, - const TimeTicks& timestamp, - int num_args, - const char** arg_names, - const unsigned char* arg_types, - const unsigned long long* arg_values, - std::unique_ptr<ConvertableToTraceFormat>* convertable_values, - unsigned int flags); - TraceEventHandle AddTraceEventWithThreadIdAndTimestamp( - char phase, - const unsigned char* category_group_enabled, - const char* name, - const char* scope, - unsigned long long id, - unsigned long long bind_id, - int thread_id, - const TimeTicks& timestamp, - int num_args, - const char** arg_names, - const unsigned char* arg_types, - const unsigned long long* arg_values, - std::unique_ptr<ConvertableToTraceFormat>* convertable_values, - unsigned int flags); - - // Adds a metadata event that will be written when the trace log is flushed. - void AddMetadataEvent( - const unsigned char* category_group_enabled, - const char* name, - int num_args, - const char** arg_names, - const unsigned char* arg_types, - const unsigned long long* arg_values, - std::unique_ptr<ConvertableToTraceFormat>* convertable_values, - unsigned int flags); - - void UpdateTraceEventDuration(const unsigned char* category_group_enabled, - const char* name, - TraceEventHandle handle); - - void EndFilteredEvent(const unsigned char* category_group_enabled, - const char* name, - TraceEventHandle handle); - - int process_id() const { return process_id_; } - - uint64_t MangleEventId(uint64_t id); - - // Exposed for unittesting: - - // Testing factory for TraceEventFilter. - typedef std::unique_ptr<TraceEventFilter> (*FilterFactoryForTesting)( - const std::string& /* predicate_name */); - void SetFilterFactoryForTesting(FilterFactoryForTesting factory) { - filter_factory_for_testing_ = factory; - } - - // Allows deleting our singleton instance. - static void DeleteForTesting(); - - // Allow tests to inspect TraceEvents. - TraceEvent* GetEventByHandle(TraceEventHandle handle); - - void SetProcessID(int process_id); - - // Process sort indices, if set, override the order of a process will appear - // relative to other processes in the trace viewer. Processes are sorted first - // on their sort index, ascending, then by their name, and then tid. - void SetProcessSortIndex(int sort_index); - - // Sets the name of the process. |process_name| should be a string literal - // since it is a whitelisted argument for background field trials. - void SetProcessName(const char* process_name); - - // Processes can have labels in addition to their names. Use labels, for - // instance, to list out the web page titles that a process is handling. - void UpdateProcessLabel(int label_id, const std::string& current_label); - void RemoveProcessLabel(int label_id); - - // Thread sort indices, if set, override the order of a thread will appear - // within its process in the trace viewer. Threads are sorted first on their - // sort index, ascending, then by their name, and then tid. - void SetThreadSortIndex(PlatformThreadId thread_id, int sort_index); - - // Allow setting an offset between the current TimeTicks time and the time - // that should be reported. - void SetTimeOffset(TimeDelta offset); - - size_t GetObserverCountForTest() const; - - // Call this method if the current thread may block the message loop to - // prevent the thread from using the thread-local buffer because the thread - // may not handle the flush request in time causing lost of unflushed events. - void SetCurrentThreadBlocksMessageLoop(); - -#if defined(OS_WIN) - // This function is called by the ETW exporting module whenever the ETW - // keyword (flags) changes. This keyword indicates which categories should be - // exported, so whenever it changes, we adjust accordingly. - void UpdateETWCategoryGroupEnabledFlags(); -#endif - - private: - typedef unsigned int InternalTraceOptions; - - FRIEND_TEST_ALL_PREFIXES(TraceEventTestFixture, - TraceBufferRingBufferGetReturnChunk); - FRIEND_TEST_ALL_PREFIXES(TraceEventTestFixture, - TraceBufferRingBufferHalfIteration); - FRIEND_TEST_ALL_PREFIXES(TraceEventTestFixture, - TraceBufferRingBufferFullIteration); - FRIEND_TEST_ALL_PREFIXES(TraceEventTestFixture, TraceBufferVectorReportFull); - FRIEND_TEST_ALL_PREFIXES(TraceEventTestFixture, - ConvertTraceConfigToInternalOptions); - FRIEND_TEST_ALL_PREFIXES(TraceEventTestFixture, - TraceRecordAsMuchAsPossibleMode); - - // This allows constructor and destructor to be private and usable only - // by the Singleton class. - friend struct DefaultSingletonTraits<TraceLog>; - - // MemoryDumpProvider implementation. - bool OnMemoryDump(const MemoryDumpArgs& args, - ProcessMemoryDump* pmd) override; - - // Enable/disable each category group based on the current mode_, - // category_filter_ and event_filters_enabled_. - // Enable the category group in the recording mode if category_filter_ matches - // the category group, is not null. Enable category for filtering if any - // filter in event_filters_enabled_ enables it. - void UpdateCategoryRegistry(); - void UpdateCategoryState(TraceCategory* category); - - void CreateFiltersForTraceConfig(); - - // Configure synthetic delays based on the values set in the current - // trace config. - void UpdateSyntheticDelaysFromTraceConfig(); - - InternalTraceOptions GetInternalOptionsFromTraceConfig( - const TraceConfig& config); - - class ThreadLocalEventBuffer; - class OptionalAutoLock; - struct RegisteredAsyncObserver; - - TraceLog(); - ~TraceLog() override; - void AddMetadataEventsWhileLocked(); - - InternalTraceOptions trace_options() const { - return static_cast<InternalTraceOptions>( - subtle::NoBarrier_Load(&trace_options_)); - } - - TraceBuffer* trace_buffer() const { return logged_events_.get(); } - TraceBuffer* CreateTraceBuffer(); - - std::string EventToConsoleMessage(unsigned char phase, - const TimeTicks& timestamp, - TraceEvent* trace_event); - - TraceEvent* AddEventToThreadSharedChunkWhileLocked(TraceEventHandle* handle, - bool check_buffer_is_full); - void CheckIfBufferIsFullWhileLocked(); - void SetDisabledWhileLocked(uint8_t modes); - - TraceEvent* GetEventByHandleInternal(TraceEventHandle handle, - OptionalAutoLock* lock); - - void FlushInternal(const OutputCallback& cb, - bool use_worker_thread, - bool discard_events); - - // |generation| is used in the following callbacks to check if the callback - // is called for the flush of the current |logged_events_|. - void FlushCurrentThread(int generation, bool discard_events); - // Usually it runs on a different thread. - static void ConvertTraceEventsToTraceFormat( - std::unique_ptr<TraceBuffer> logged_events, - const TraceLog::OutputCallback& flush_output_callback, - const ArgumentFilterPredicate& argument_filter_predicate); - void FinishFlush(int generation, bool discard_events); - void OnFlushTimeout(int generation, bool discard_events); - - int generation() const { - return static_cast<int>(subtle::NoBarrier_Load(&generation_)); - } - bool CheckGeneration(int generation) const { - return generation == this->generation(); - } - void UseNextTraceBuffer(); - - TimeTicks OffsetNow() const { return OffsetTimestamp(TimeTicks::Now()); } - TimeTicks OffsetTimestamp(const TimeTicks& timestamp) const { - return timestamp - time_offset_; - } - - // Internal representation of trace options since we store the currently used - // trace option as an AtomicWord. - static const InternalTraceOptions kInternalNone; - static const InternalTraceOptions kInternalRecordUntilFull; - static const InternalTraceOptions kInternalRecordContinuously; - static const InternalTraceOptions kInternalEchoToConsole; - static const InternalTraceOptions kInternalRecordAsMuchAsPossible; - static const InternalTraceOptions kInternalEnableArgumentFilter; - - // This lock protects TraceLog member accesses (except for members protected - // by thread_info_lock_) from arbitrary threads. - mutable Lock lock_; - // This lock protects accesses to thread_names_, thread_event_start_times_ - // and thread_colors_. - Lock thread_info_lock_; - uint8_t enabled_modes_; // See TraceLog::Mode. - int num_traces_recorded_; - std::unique_ptr<TraceBuffer> logged_events_; - std::vector<std::unique_ptr<TraceEvent>> metadata_events_; - bool dispatching_to_observer_list_; - std::vector<EnabledStateObserver*> enabled_state_observer_list_; - std::map<AsyncEnabledStateObserver*, RegisteredAsyncObserver> - async_observers_; - - std::string process_name_; - base::hash_map<int, std::string> process_labels_; - int process_sort_index_; - base::hash_map<int, int> thread_sort_indices_; - base::hash_map<int, std::string> thread_names_; - - // The following two maps are used only when ECHO_TO_CONSOLE. - base::hash_map<int, std::stack<TimeTicks>> thread_event_start_times_; - base::hash_map<std::string, int> thread_colors_; - - TimeTicks buffer_limit_reached_timestamp_; - - // XORed with TraceID to make it unlikely to collide with other processes. - unsigned long long process_id_hash_; - - int process_id_; - - TimeDelta time_offset_; - - subtle::AtomicWord /* Options */ trace_options_; - - TraceConfig trace_config_; - TraceConfig::EventFilters enabled_event_filters_; - - ThreadLocalPointer<ThreadLocalEventBuffer> thread_local_event_buffer_; - ThreadLocalBoolean thread_blocks_message_loop_; - ThreadLocalBoolean thread_is_in_trace_event_; - - // Contains the message loops of threads that have had at least one event - // added into the local event buffer. Not using SingleThreadTaskRunner - // because we need to know the life time of the message loops. - hash_set<MessageLoop*> thread_message_loops_; - - // For events which can't be added into the thread local buffer, e.g. events - // from threads without a message loop. - std::unique_ptr<TraceBufferChunk> thread_shared_chunk_; - size_t thread_shared_chunk_index_; - - // Set when asynchronous Flush is in progress. - OutputCallback flush_output_callback_; - scoped_refptr<SingleThreadTaskRunner> flush_task_runner_; - ArgumentFilterPredicate argument_filter_predicate_; - subtle::AtomicWord generation_; - bool use_worker_thread_; - - FilterFactoryForTesting filter_factory_for_testing_; - - DISALLOW_COPY_AND_ASSIGN(TraceLog); -}; - -} // namespace trace_event -} // namespace base - -#endif // BASE_TRACE_EVENT_TRACE_LOG_H_ diff --git a/base/trace_event/trace_log_constants.cc b/base/trace_event/trace_log_constants.cc deleted file mode 100644 index 65dca2e4d6..0000000000 --- a/base/trace_event/trace_log_constants.cc +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/trace_event/trace_log.h" - -namespace base { -namespace trace_event { - -// Constant used by TraceLog's internal implementation of trace_option. -const TraceLog::InternalTraceOptions - TraceLog::kInternalNone = 0; -const TraceLog::InternalTraceOptions - TraceLog::kInternalRecordUntilFull = 1 << 0; -const TraceLog::InternalTraceOptions - TraceLog::kInternalRecordContinuously = 1 << 1; -// 1 << 2 is reserved for the DEPRECATED kInternalEnableSampling. DO NOT USE. -const TraceLog::InternalTraceOptions - TraceLog::kInternalEchoToConsole = 1 << 3; -const TraceLog::InternalTraceOptions - TraceLog::kInternalRecordAsMuchAsPossible = 1 << 4; -const TraceLog::InternalTraceOptions - TraceLog::kInternalEnableArgumentFilter = 1 << 5; - -} // namespace trace_event -} // namespace base diff --git a/libchrome_tools/patch/task_scheduler.patch b/libchrome_tools/patch/task_scheduler.patch index c32520ba41..39a4ead16d 100644 --- a/libchrome_tools/patch/task_scheduler.patch +++ b/libchrome_tools/patch/task_scheduler.patch @@ -119,34 +119,3 @@ } else { inner_->CleanupForTesting(); } ---- a/base/trace_event/trace_log.cc -+++ b/base/trace_event/trace_log.cc -@@ -27,7 +27,10 @@ - #include "base/strings/string_tokenizer.h" - #include "base/strings/stringprintf.h" - #include "base/sys_info.h" -+// post_task.h pulls in a lot of code not needed on Arc++. -+#if 0 - #include "base/task_scheduler/post_task.h" -+#endif - #include "base/threading/platform_thread.h" - #include "base/threading/thread_id_name_manager.h" - #include "base/threading/thread_task_runner_handle.h" -@@ -968,6 +971,7 @@ void TraceLog::FinishFlush(int generatio - } - - if (use_worker_thread_) { -+#if 0 - base::PostTaskWithTraits( - FROM_HERE, base::TaskTraits() - .MayBlock() -@@ -978,6 +982,9 @@ void TraceLog::FinishFlush(int generatio - Passed(&previous_logged_events), flush_output_callback, - argument_filter_predicate)); - return; -+#else -+ NOTREACHED(); -+#endif - } - - ConvertTraceEventsToTraceFormat(std::move(previous_logged_events), diff --git a/libchrome_tools/patch/trace_event.patch b/libchrome_tools/patch/trace_event.patch new file mode 100644 index 0000000000..514ef53bbc --- /dev/null +++ b/libchrome_tools/patch/trace_event.patch @@ -0,0 +1,191 @@ +--- a/base/trace_event/trace_event.h ++++ b/base/trace_event/trace_event.h +@@ -5,6 +5,43 @@ + #ifndef BASE_TRACE_EVENT_TRACE_EVENT_H_ + #define BASE_TRACE_EVENT_TRACE_EVENT_H_ + ++// Replace with stub implementation. ++#if 1 ++#include "base/trace_event/common/trace_event_common.h" ++#include "base/trace_event/heap_profiler.h" ++ ++// To avoid -Wunused-* errors, eat expression by macro. ++namespace libchrome_internal { ++template <typename... Args> void Ignore(Args&&... args) {} ++} ++#define INTERNAL_IGNORE(...) \ ++ (false ? libchrome_internal::Ignore(__VA_ARGS__) : (void) 0) ++ ++// Body is effectively empty. ++#define INTERNAL_TRACE_EVENT_ADD_SCOPED(...) INTERNAL_IGNORE(__VA_ARGS__) ++#define INTERNAL_TRACE_TASK_EXECUTION(...) ++#define INTERNAL_TRACE_EVENT_ADD_SCOPED_WITH_FLOW(...) \ ++ INTERNAL_IGNORE(__VA_ARGS__) ++#define TRACE_ID_MANGLE(val) (val) ++ ++namespace base { ++namespace trace_event { ++ ++class TraceLog { ++ public: ++ static TraceLog* GetInstance() { ++ static TraceLog instance; ++ return &instance; ++ } ++ ++ pid_t process_id() { return 0; } ++ void SetCurrentThreadBlocksMessageLoop() {} ++}; ++ ++} // namespace trace_event ++} // namespace base ++#else ++ + // This header file defines implementation details of how the trace macros in + // trace_event_common.h collect and store trace events. Anything not + // implementation-specific should go in trace_event_common.h instead of here. +@@ -1115,4 +1152,5 @@ template<typename IDType> class TraceSco + } // namespace trace_event + } // namespace base + ++#endif + #endif // BASE_TRACE_EVENT_TRACE_EVENT_H_ +--- a/base/trace_event/heap_profiler.h ++++ b/base/trace_event/heap_profiler.h +@@ -5,6 +5,22 @@ + #ifndef BASE_TRACE_EVENT_HEAP_PROFILER_H + #define BASE_TRACE_EVENT_HEAP_PROFILER_H + ++// Replace with stub implementation. ++#if 1 ++#define TRACE_HEAP_PROFILER_API_SCOPED_TASK_EXECUTION \ ++ trace_event_internal::HeapProfilerScopedTaskExecutionTracker ++ ++namespace trace_event_internal { ++ ++class HeapProfilerScopedTaskExecutionTracker { ++ public: ++ explicit HeapProfilerScopedTaskExecutionTracker(const char*) {} ++}; ++ ++} // namespace trace_event_internal ++ ++#else ++ + #include "base/compiler_specific.h" + #include "base/trace_event/heap_profiler_allocation_context_tracker.h" + +@@ -86,4 +102,5 @@ class BASE_EXPORT HeapProfilerScopedIgno + + } // namespace trace_event_internal + ++#endif + #endif // BASE_TRACE_EVENT_HEAP_PROFILER_H +--- a/base/test/test_pending_task.h ++++ b/base/test/test_pending_task.h +@@ -10,7 +10,8 @@ + #include "base/callback.h" + #include "base/location.h" + #include "base/time/time.h" +-#include "base/trace_event/trace_event_argument.h" ++// Unsupported in libchrome. ++// #include "base/trace_event/trace_event_argument.h" + + namespace base { + +@@ -58,10 +59,13 @@ struct TestPendingTask { + TimeDelta delay; + TestNestability nestability; + ++// Unsupported in libchrome. ++#if 0 + // Functions for using test pending task with tracing, useful in unit + // testing. + void AsValueInto(base::trace_event::TracedValue* state) const; + std::unique_ptr<base::trace_event::ConvertableToTraceFormat> AsValue() const; ++#endif + std::string ToString() const; + + private: +--- a/base/test/test_pending_task.cc ++++ b/base/test/test_pending_task.cc +@@ -38,6 +38,8 @@ bool TestPendingTask::ShouldRunBefore(co + + TestPendingTask::~TestPendingTask() {} + ++// Unsupported in libchrome. ++#if 0 + void TestPendingTask::AsValueInto(base::trace_event::TracedValue* state) const { + state->SetInteger("run_at", GetTimeToRun().ToInternalValue()); + state->SetString("posting_function", location.ToString()); +@@ -61,10 +63,14 @@ TestPendingTask::AsValue() const { + AsValueInto(state.get()); + return std::move(state); + } ++#endif + + std::string TestPendingTask::ToString() const { + std::string output("TestPendingTask("); ++// Unsupported in libchrome. ++#if 0 + AsValue()->AppendAsTraceFormat(&output); ++#endif + output += ")"; + return output; + } +--- a/base/threading/thread_id_name_manager.cc ++++ b/base/threading/thread_id_name_manager.cc +@@ -10,7 +10,8 @@ + #include "base/logging.h" + #include "base/memory/singleton.h" + #include "base/strings/string_util.h" +-#include "base/trace_event/heap_profiler_allocation_context_tracker.h" ++// Unsupported in libchrome. ++// #include "base/trace_event/heap_profiler_allocation_context_tracker.h" + + namespace base { + namespace { +@@ -80,8 +81,9 @@ void ThreadIdNameManager::SetName(Platfo + // call GetName(which holds a lock) during the first allocation because it can + // cause a deadlock when the first allocation happens in the + // ThreadIdNameManager itself when holding the lock. +- trace_event::AllocationContextTracker::SetCurrentThreadName( +- leaked_str->c_str()); ++ // Unsupported in libchrome. ++ // trace_event::AllocationContextTracker::SetCurrentThreadName( ++ // leaked_str->c_str()); + } + + const char* ThreadIdNameManager::GetName(PlatformThreadId id) { +--- a/base/memory/shared_memory_posix.cc ++++ b/base/memory/shared_memory_posix.cc +@@ -15,7 +15,8 @@ + #include "base/files/scoped_file.h" + #include "base/logging.h" + #include "base/memory/shared_memory_helper.h" +-#include "base/memory/shared_memory_tracker.h" ++// Unsupported in libchrome. ++// #include "base/memory/shared_memory_tracker.h" + #include "base/posix/eintr_wrapper.h" + #include "base/posix/safe_strerror.h" + #include "base/process/process_metrics.h" +@@ -288,7 +291,8 @@ bool SharedMemory::MapAt(off_t offset, s + DCHECK_EQ(0U, + reinterpret_cast<uintptr_t>(memory_) & + (SharedMemory::MAP_MINIMUM_ALIGNMENT - 1)); +- SharedMemoryTracker::GetInstance()->IncrementMemoryUsage(*this); ++ // Unsupported in libchrome. ++ // SharedMemoryTracker::GetInstance()->IncrementMemoryUsage(*this); + } else { + memory_ = NULL; + } +@@ -301,7 +305,8 @@ bool SharedMemory::Unmap() { + return false; + + munmap(memory_, mapped_size_); +- SharedMemoryTracker::GetInstance()->DecrementMemoryUsage(*this); ++ // Unsupported in libchrome. ++ // SharedMemoryTracker::GetInstance()->DecrementMemoryUsage(*this); + memory_ = NULL; + mapped_size_ = 0; + return true; |