summaryrefslogtreecommitdiff
path: root/base/trace_event
diff options
context:
space:
mode:
authorJakub Pawlowski <jpawlowski@google.com>2017-03-14 10:55:53 -0700
committerJakub Pawlowski <jpawlowski@google.com>2017-12-21 08:29:21 -0800
commitfe2f52931e8e50253d06329d7bf0a4164c7ba580 (patch)
tree6bb5a209c8ffd63c3c197e938ad4a62b7328b83b /base/trace_event
parent70cd4fac31a9b0865dab6574540f70cc103337dc (diff)
downloadlibchrome-fe2f52931e8e50253d06329d7bf0a4164c7ba580.tar.gz
Uprev the library to r462023 from Chromium
This merge was done against r462023 which corresponds to git commit 32eb7c31af9cab6231f0d3d05206072079177605 from Apr 05, 2017 Previous attempt, in commit bf8c17f71511c1e90cd8cccfe71f0852c566bd3b was badly squashed, causing automated test failure in system/bt. Test: manually ran all test from system/bt that failed on previous attempt, plus libchrome_unittest Change-Id: I539a868af1d06baad3e80a89b8c16fb3d86ab77a
Diffstat (limited to 'base/trace_event')
-rw-r--r--base/trace_event/heap_profiler_allocation_context_tracker_unittest.cc5
-rw-r--r--base/trace_event/malloc_dump_provider.cc10
-rw-r--r--base/trace_event/memory_allocator_dump.cc5
-rw-r--r--base/trace_event/memory_allocator_dump.h11
-rw-r--r--base/trace_event/memory_allocator_dump_unittest.cc10
-rw-r--r--base/trace_event/memory_dump_manager.cc178
-rw-r--r--base/trace_event/memory_dump_manager.h42
-rw-r--r--base/trace_event/memory_dump_manager_unittest.cc37
-rw-r--r--base/trace_event/memory_dump_request_args.cc4
-rw-r--r--base/trace_event/memory_dump_request_args.h29
-rw-r--r--base/trace_event/memory_dump_scheduler.cc196
-rw-r--r--base/trace_event/memory_dump_scheduler.h64
-rw-r--r--base/trace_event/memory_dump_scheduler_unittest.cc101
-rw-r--r--base/trace_event/memory_infra_background_whitelist.cc68
-rw-r--r--base/trace_event/trace_config.cc369
-rw-r--r--base/trace_event/trace_config.h62
-rw-r--r--base/trace_event/trace_config_category_filter.cc298
-rw-r--r--base/trace_event/trace_config_category_filter.h86
-rw-r--r--base/trace_event/trace_config_unittest.cc131
-rw-r--r--base/trace_event/trace_event_unittest.cc37
-rw-r--r--base/trace_event/trace_log.cc14
21 files changed, 1086 insertions, 671 deletions
diff --git a/base/trace_event/heap_profiler_allocation_context_tracker_unittest.cc b/base/trace_event/heap_profiler_allocation_context_tracker_unittest.cc
index 577f50043d..6317886b0d 100644
--- a/base/trace_event/heap_profiler_allocation_context_tracker_unittest.cc
+++ b/base/trace_event/heap_profiler_allocation_context_tracker_unittest.cc
@@ -34,7 +34,9 @@ const char kFilteringTraceConfig[] =
" \"excluded_categories\": [],"
" \"filter_args\": {},"
" \"filter_predicate\": \"heap_profiler_predicate\","
- " \"included_categories\": [\"*\"]"
+ " \"included_categories\": ["
+ " \"*\","
+ " \"" TRACE_DISABLED_BY_DEFAULT("Testing") "\"]"
" }"
" ]"
"}";
@@ -122,6 +124,7 @@ TEST_F(AllocationContextTrackerTest, PseudoStackScopedTrace) {
}
{
+ 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);
diff --git a/base/trace_event/malloc_dump_provider.cc b/base/trace_event/malloc_dump_provider.cc
index 5f5a80af3b..d78de9b548 100644
--- a/base/trace_event/malloc_dump_provider.cc
+++ b/base/trace_event/malloc_dump_provider.cc
@@ -54,10 +54,10 @@ void* HookZeroInitAlloc(const AllocatorDispatch* self,
return ptr;
}
-void* HookllocAligned(const AllocatorDispatch* self,
- size_t alignment,
- size_t size,
- void* context) {
+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)
@@ -129,7 +129,7 @@ void HookFreeDefiniteSize(const AllocatorDispatch* self,
AllocatorDispatch g_allocator_hooks = {
&HookAlloc, /* alloc_function */
&HookZeroInitAlloc, /* alloc_zero_initialized_function */
- &HookllocAligned, /* alloc_aligned_function */
+ &HookAllocAligned, /* alloc_aligned_function */
&HookRealloc, /* realloc_function */
&HookFree, /* free_function */
&HookGetSizeEstimate, /* get_size_estimate_function */
diff --git a/base/trace_event/memory_allocator_dump.cc b/base/trace_event/memory_allocator_dump.cc
index 7583763889..2692521c09 100644
--- a/base/trace_event/memory_allocator_dump.cc
+++ b/base/trace_event/memory_allocator_dump.cc
@@ -29,7 +29,8 @@ MemoryAllocatorDump::MemoryAllocatorDump(const std::string& absolute_name,
process_memory_dump_(process_memory_dump),
attributes_(new TracedValue),
guid_(guid),
- flags_(Flags::DEFAULT) {
+ flags_(Flags::DEFAULT),
+ size_(0) {
// The |absolute_name| cannot be empty.
DCHECK(!absolute_name.empty());
@@ -59,6 +60,8 @@ 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);
diff --git a/base/trace_event/memory_allocator_dump.h b/base/trace_event/memory_allocator_dump.h
index c781f071bb..99ff114e5c 100644
--- a/base/trace_event/memory_allocator_dump.h
+++ b/base/trace_event/memory_allocator_dump.h
@@ -11,6 +11,7 @@
#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"
@@ -85,11 +86,21 @@ class BASE_EXPORT MemoryAllocatorDump {
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.
diff --git a/base/trace_event/memory_allocator_dump_unittest.cc b/base/trace_event/memory_allocator_dump_unittest.cc
index 1bf9715917..e1818f6eec 100644
--- a/base/trace_event/memory_allocator_dump_unittest.cc
+++ b/base/trace_event/memory_allocator_dump_unittest.cc
@@ -172,6 +172,16 @@ TEST(MemoryAllocatorDumpTest, DumpIntoProcessMemoryDump) {
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) {
diff --git a/base/trace_event/memory_dump_manager.cc b/base/trace_event/memory_dump_manager.cc
index 5a54a773c5..a74b95634d 100644
--- a/base/trace_event/memory_dump_manager.cc
+++ b/base/trace_event/memory_dump_manager.cc
@@ -4,6 +4,9 @@
#include "base/trace_event/memory_dump_manager.h"
+#include <inttypes.h>
+#include <stdio.h>
+
#include <algorithm>
#include <utility>
@@ -17,6 +20,8 @@
#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"
@@ -80,9 +85,12 @@ const char* const kStrictThreadCheckBlacklist[] = {
void OnGlobalDumpDone(MemoryDumpCallback wrapped_callback,
uint64_t dump_guid,
bool success) {
- TRACE_EVENT_NESTABLE_ASYNC_END1(
- MemoryDumpManager::kTraceCategory, "GlobalMemoryDump",
- TRACE_ID_MANGLE(dump_guid), "success", 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);
@@ -155,9 +163,7 @@ void MemoryDumpManager::SetInstanceForTesting(MemoryDumpManager* instance) {
}
MemoryDumpManager::MemoryDumpManager()
- : delegate_(nullptr),
- is_coordinator_(false),
- memory_tracing_enabled_(0),
+ : memory_tracing_enabled_(0),
tracing_process_id_(kInvalidTracingProcessId),
dumper_registrations_ignored_for_testing_(false),
heap_profiling_enabled_(false) {
@@ -214,14 +220,13 @@ void MemoryDumpManager::EnableHeapProfilingIfNeeded() {
heap_profiling_enabled_ = true;
}
-void MemoryDumpManager::Initialize(MemoryDumpManagerDelegate* delegate,
- bool is_coordinator) {
+void MemoryDumpManager::Initialize(
+ std::unique_ptr<MemoryDumpManagerDelegate> delegate) {
{
AutoLock lock(lock_);
DCHECK(delegate);
DCHECK(!delegate_);
- delegate_ = delegate;
- is_coordinator_ = is_coordinator;
+ delegate_ = std::move(delegate);
EnableHeapProfilingIfNeeded();
}
@@ -243,11 +248,19 @@ void MemoryDumpManager::Initialize(MemoryDumpManagerDelegate* delegate,
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.AddIncludedCategory("*");
- heap_profiler_filter_config.AddIncludedCategory(
- MemoryDumpManager::kTraceCategory);
+ heap_profiler_filter_config.SetCategoryFilter(category_filter);
+
TraceConfig::EventFilters filters;
filters.push_back(heap_profiler_filter_config);
TraceConfig filtering_trace_config;
@@ -421,7 +434,7 @@ void MemoryDumpManager::RegisterPollingMDPOnDumpThread(
// 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)
- dump_scheduler_->NotifyPollingSupported();
+ MemoryDumpScheduler::GetInstance()->EnablePollingIfNeeded();
}
void MemoryDumpManager::UnregisterPollingMDPOnDumpThread(
@@ -456,25 +469,16 @@ void MemoryDumpManager::RequestGlobalDump(
// 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_BEGIN0(kTraceCategory, "GlobalMemoryDump",
- TRACE_ID_MANGLE(guid));
+ 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);
- // Technically there is no need to grab the |lock_| here as the delegate is
- // long-lived and can only be set by Initialize(), which is locked and
- // necessarily happens before memory_tracing_enabled_ == true.
- // Not taking the |lock_|, though, is lakely make TSan barf and, at this point
- // (memory-infra is enabled) we're not in the fast-path anymore.
- MemoryDumpManagerDelegate* delegate;
- {
- AutoLock lock(lock_);
- delegate = delegate_;
- }
-
// 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);
+ delegate_->RequestGlobalMemoryDump(args, wrapped_callback);
}
void MemoryDumpManager::RequestGlobalDump(
@@ -483,10 +487,24 @@ void MemoryDumpManager::RequestGlobalDump(
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) {
- TRACE_EVENT_NESTABLE_ASYNC_BEGIN0(kTraceCategory, "ProcessMemoryDump",
- TRACE_ID_MANGLE(args.dump_guid));
+ 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
@@ -515,14 +533,9 @@ void MemoryDumpManager::CreateProcessDump(const MemoryDumpRequestArgs& args,
CHECK(!session_state_ ||
session_state_->IsDumpModeAllowed(args.level_of_detail));
- if (dump_scheduler_)
- dump_scheduler_->NotifyDumpTriggered();
+ MemoryDumpScheduler::GetInstance()->NotifyDumpTriggered();
}
- TRACE_EVENT_WITH_FLOW0(kTraceCategory, "MemoryDumpManager::CreateProcessDump",
- TRACE_ID_MANGLE(args.dump_guid),
- TRACE_EVENT_FLAG_FLOW_OUT);
-
// Start the process dump. This involves task runner hops as specified by the
// MemoryDumpProvider(s) in RegisterDumpProvider()).
SetupNextMemoryDump(std::move(pmd_async_state));
@@ -666,11 +679,8 @@ void MemoryDumpManager::InvokeOnMemoryDump(
if (should_dump) {
// Invoke the dump provider.
- TRACE_EVENT_WITH_FLOW1(kTraceCategory,
- "MemoryDumpManager::InvokeOnMemoryDump",
- TRACE_ID_MANGLE(pmd_async_state->req_args.dump_guid),
- TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT,
- "dump_provider.name", mdpinfo->name);
+ 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
@@ -722,6 +732,18 @@ bool MemoryDumpManager::PollFastMemoryTotal(uint64_t* memory_total) {
}
// 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;
@@ -736,9 +758,11 @@ void MemoryDumpManager::FinalizeDumpAndAddToTrace(
return;
}
- TRACE_EVENT_WITH_FLOW0(kTraceCategory,
- "MemoryDumpManager::FinalizeDumpAndAddToTrace",
- TRACE_ID_MANGLE(dump_guid), TRACE_EVENT_FLAG_FLOW_IN);
+ 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.
@@ -760,6 +784,30 @@ void MemoryDumpManager::FinalizeDumpAndAddToTrace(
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;
@@ -776,7 +824,7 @@ void MemoryDumpManager::FinalizeDumpAndAddToTrace(
}
TRACE_EVENT_NESTABLE_ASYNC_END0(kTraceCategory, "ProcessMemoryDump",
- TRACE_ID_MANGLE(dump_guid));
+ TRACE_ID_LOCAL(dump_guid));
}
void MemoryDumpManager::OnTraceLogEnabled() {
@@ -829,18 +877,6 @@ void MemoryDumpManager::OnTraceLogEnabled() {
session_state, &MemoryDumpSessionState::type_name_deduplicator));
}
- std::unique_ptr<MemoryDumpScheduler> dump_scheduler(
- new MemoryDumpScheduler(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);
- }
-
{
AutoLock lock(lock_);
@@ -849,7 +885,6 @@ void MemoryDumpManager::OnTraceLogEnabled() {
DCHECK(!dump_thread_);
dump_thread_ = std::move(dump_thread);
- dump_scheduler_ = std::move(dump_scheduler);
subtle::NoBarrier_Store(&memory_tracing_enabled_, 1);
@@ -858,15 +893,28 @@ void MemoryDumpManager::OnTraceLogEnabled() {
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_->NotifyPollingSupported();
+ dump_scheduler->EnablePollingIfNeeded();
// Only coordinator process triggers periodic global memory dumps.
- if (is_coordinator_)
- dump_scheduler_->NotifyPeriodicTriggerSupported();
+ if (delegate_->IsCoordinator())
+ dump_scheduler->EnablePeriodicTriggerIfNeeded();
}
}
@@ -879,14 +927,12 @@ void MemoryDumpManager::OnTraceLogDisabled() {
return;
subtle::NoBarrier_Store(&memory_tracing_enabled_, 0);
std::unique_ptr<Thread> dump_thread;
- std::unique_ptr<MemoryDumpScheduler> scheduler;
{
AutoLock lock(lock_);
dump_thread = std::move(dump_thread_);
session_state_ = nullptr;
- scheduler = std::move(dump_scheduler_);
+ MemoryDumpScheduler::GetInstance()->DisableAllTriggers();
}
- scheduler->DisableAllTriggers();
// Thread stops are blocking and must be performed outside of the |lock_|
// or will deadlock (e.g., if SetupNextMemoryDump() tries to acquire it).
@@ -910,10 +956,6 @@ bool MemoryDumpManager::IsDumpModeAllowed(MemoryDumpLevelOfDetail dump_mode) {
return session_state_->IsDumpModeAllowed(dump_mode);
}
-uint64_t MemoryDumpManager::GetTracingProcessId() const {
- return delegate_->GetTracingProcessId();
-}
-
MemoryDumpManager::MemoryDumpProviderInfo::MemoryDumpProviderInfo(
MemoryDumpProvider* dump_provider,
const char* name,
diff --git a/base/trace_event/memory_dump_manager.h b/base/trace_event/memory_dump_manager.h
index 92cc2f401b..ebee048691 100644
--- a/base/trace_event/memory_dump_manager.h
+++ b/base/trace_event/memory_dump_manager.h
@@ -18,10 +18,19 @@
#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_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;
@@ -54,13 +63,10 @@ class BASE_EXPORT MemoryDumpManager : public TraceLog::EnabledStateObserver {
// On the other side, the MemoryDumpManager will not be fully operational
// (i.e. will NACK any RequestGlobalMemoryDump()) until initialized.
// Arguments:
- // is_coordinator: if true this MemoryDumpManager instance will act as a
- // coordinator and schedule periodic dumps (if enabled via TraceConfig);
- // false when the MemoryDumpManager is initialized in a slave process.
// delegate: inversion-of-control interface for embedder-specific behaviors
// (multiprocess handshaking). See the lifetime and thread-safety
// requirements in the |MemoryDumpManagerDelegate| docstring.
- void Initialize(MemoryDumpManagerDelegate* delegate, bool is_coordinator);
+ void Initialize(std::unique_ptr<MemoryDumpManagerDelegate> delegate);
// (Un)Registers a MemoryDumpProvider instance.
// Args:
@@ -123,6 +129,9 @@ class BASE_EXPORT MemoryDumpManager : public TraceLog::EnabledStateObserver {
// 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.
@@ -135,7 +144,10 @@ class BASE_EXPORT MemoryDumpManager : public TraceLog::EnabledStateObserver {
// 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;
+ 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.
@@ -156,6 +168,7 @@ class BASE_EXPORT MemoryDumpManager : public TraceLog::EnabledStateObserver {
friend class MemoryDumpManagerDelegate;
friend class MemoryDumpManagerTest;
friend class MemoryDumpScheduler;
+ friend class memory_instrumentation::MemoryDumpManagerDelegateImplTest;
// Descriptor used to hold information about registered MDPs.
// Some important considerations about lifetime of this object:
@@ -285,6 +298,7 @@ class BASE_EXPORT MemoryDumpManager : public TraceLog::EnabledStateObserver {
~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);
@@ -348,10 +362,7 @@ class BASE_EXPORT MemoryDumpManager : public TraceLog::EnabledStateObserver {
std::unordered_set<StringPiece, StringPieceHash>
strict_thread_check_blacklist_;
- MemoryDumpManagerDelegate* delegate_; // Not owned.
-
- // When true, this instance is in charge of coordinating periodic dumps.
- bool is_coordinator_;
+ 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.
@@ -361,9 +372,6 @@ class BASE_EXPORT MemoryDumpManager : public TraceLog::EnabledStateObserver {
// dump_providers_enabled_ list) when tracing is not enabled.
subtle::AtomicWord memory_tracing_enabled_;
- // For triggering memory dumps.
- std::unique_ptr<MemoryDumpScheduler> dump_scheduler_;
-
// Thread used for MemoryDumpProviders which don't specify a task runner
// affinity.
std::unique_ptr<Thread> dump_thread_;
@@ -385,17 +393,15 @@ class BASE_EXPORT MemoryDumpManager : public TraceLog::EnabledStateObserver {
// 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;
- // Returns tracing process id of the current process. This is used by
- // MemoryDumpManager::GetTracingProcessId.
- virtual uint64_t GetTracingProcessId() const = 0;
+ virtual bool IsCoordinator() const = 0;
protected:
- MemoryDumpManagerDelegate() {}
- virtual ~MemoryDumpManagerDelegate() {}
-
void CreateProcessDump(const MemoryDumpRequestArgs& args,
const MemoryDumpCallback& callback) {
MemoryDumpManager::GetInstance()->CreateProcessDump(args, callback);
diff --git a/base/trace_event/memory_dump_manager_unittest.cc b/base/trace_event/memory_dump_manager_unittest.cc
index 51d41943fb..e037fd4982 100644
--- a/base/trace_event/memory_dump_manager_unittest.cc
+++ b/base/trace_event/memory_dump_manager_unittest.cc
@@ -7,9 +7,11 @@
#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"
@@ -102,10 +104,10 @@ void OnTraceDataCollected(Closure quit_closure,
// Posts |task| to |task_runner| and blocks until it is executed.
void PostTaskAndWait(const tracked_objects::Location& from_here,
SequencedTaskRunner* task_runner,
- const base::Closure& task) {
+ base::Closure task) {
base::WaitableEvent event(WaitableEvent::ResetPolicy::MANUAL,
WaitableEvent::InitialState::NOT_SIGNALED);
- task_runner->PostTask(from_here, task);
+ 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
@@ -119,7 +121,8 @@ void PostTaskAndWait(const tracked_objects::Location& from_here,
// requests locally to the MemoryDumpManager instead of performing IPC dances.
class MemoryDumpManagerDelegateForTesting : public MemoryDumpManagerDelegate {
public:
- MemoryDumpManagerDelegateForTesting() {
+ MemoryDumpManagerDelegateForTesting(bool is_coordinator)
+ : is_coordinator_(is_coordinator) {
ON_CALL(*this, RequestGlobalMemoryDump(_, _))
.WillByDefault(Invoke(
this, &MemoryDumpManagerDelegateForTesting::CreateProcessDump));
@@ -129,13 +132,13 @@ class MemoryDumpManagerDelegateForTesting : public MemoryDumpManagerDelegate {
void(const MemoryDumpRequestArgs& args,
const MemoryDumpCallback& callback));
- uint64_t GetTracingProcessId() const override {
- NOTREACHED();
- return MemoryDumpManager::kInvalidTracingProcessId;
- }
+ 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 {
@@ -180,19 +183,19 @@ class TestSequencedTaskRunner : public SequencedTaskRunner {
unsigned no_of_post_tasks() const { return num_of_post_tasks_; }
bool PostNonNestableDelayedTask(const tracked_objects::Location& from_here,
- const Closure& task,
+ Closure task,
TimeDelta delay) override {
NOTREACHED();
return false;
}
bool PostDelayedTask(const tracked_objects::Location& from_here,
- const Closure& task,
+ Closure task,
TimeDelta delay) override {
num_of_post_tasks_++;
if (enabled_) {
return worker_pool_.pool()->PostSequencedWorkerTask(token_, from_here,
- task);
+ std::move(task));
}
return false;
}
@@ -220,13 +223,12 @@ class MemoryDumpManagerTest : public testing::Test {
mdm_.reset(new MemoryDumpManager());
MemoryDumpManager::SetInstanceForTesting(mdm_.get());
ASSERT_EQ(mdm_.get(), MemoryDumpManager::GetInstance());
- delegate_.reset(new MemoryDumpManagerDelegateForTesting);
}
void TearDown() override {
MemoryDumpManager::SetInstanceForTesting(nullptr);
+ delegate_ = nullptr;
mdm_.reset();
- delegate_.reset();
message_loop_.reset();
TraceLog::DeleteForTesting();
}
@@ -248,7 +250,8 @@ class MemoryDumpManagerTest : public testing::Test {
protected:
void InitializeMemoryDumpManager(bool is_coordinator) {
mdm_->set_dumper_registrations_ignored_for_testing(true);
- mdm_->Initialize(delegate_.get(), is_coordinator);
+ delegate_ = new MemoryDumpManagerDelegateForTesting(is_coordinator);
+ mdm_->Initialize(base::WrapUnique(delegate_));
}
void RequestGlobalDumpAndWait(MemoryDumpType dump_type,
@@ -274,7 +277,8 @@ class MemoryDumpManagerTest : public testing::Test {
void DisableTracing() { TraceLog::GetInstance()->SetDisabled(); }
bool IsPeriodicDumpingEnabled() const {
- return mdm_->dump_scheduler_->IsPeriodicTimerRunningForTesting();
+ return MemoryDumpScheduler::GetInstance()
+ ->IsPeriodicTimerRunningForTesting();
}
int GetMaxConsecutiveFailuresCount() const {
@@ -283,7 +287,7 @@ class MemoryDumpManagerTest : public testing::Test {
const MemoryDumpProvider::Options kDefaultOptions;
std::unique_ptr<MemoryDumpManager> mdm_;
- std::unique_ptr<MemoryDumpManagerDelegateForTesting> delegate_;
+ MemoryDumpManagerDelegateForTesting* delegate_;
bool last_callback_success_;
private:
@@ -897,7 +901,6 @@ TEST_F(MemoryDumpManagerTest, InitializedAfterStartOfTracing) {
// initialization gets NACK-ed cleanly.
{
EXPECT_CALL(mdp, OnMemoryDump(_, _)).Times(0);
- EXPECT_CALL(*delegate_, RequestGlobalMemoryDump(_, _)).Times(0);
RequestGlobalDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
MemoryDumpLevelOfDetail::DETAILED);
EXPECT_FALSE(last_callback_success_);
@@ -906,9 +909,9 @@ TEST_F(MemoryDumpManagerTest, InitializedAfterStartOfTracing) {
// 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);
- InitializeMemoryDumpManager(false /* is_coordinator */);
RequestGlobalDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
MemoryDumpLevelOfDetail::DETAILED);
EXPECT_TRUE(last_callback_success_);
diff --git a/base/trace_event/memory_dump_request_args.cc b/base/trace_event/memory_dump_request_args.cc
index bf72bef5e4..f2744007d7 100644
--- a/base/trace_event/memory_dump_request_args.cc
+++ b/base/trace_event/memory_dump_request_args.cc
@@ -60,5 +60,9 @@ MemoryDumpLevelOfDetail StringToMemoryDumpLevelOfDetail(
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
index 90a866fa7a..a8b3f423ca 100644
--- a/base/trace_event/memory_dump_request_args.h
+++ b/base/trace_event/memory_dump_request_args.h
@@ -9,10 +9,12 @@
// 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 {
@@ -72,6 +74,33 @@ struct MemoryDumpArgs {
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);
diff --git a/base/trace_event/memory_dump_scheduler.cc b/base/trace_event/memory_dump_scheduler.cc
index eaa8d63661..66ea6c9f1a 100644
--- a/base/trace_event/memory_dump_scheduler.cc
+++ b/base/trace_event/memory_dump_scheduler.cc
@@ -21,108 +21,131 @@ const uint32_t kMemoryTotalsPollingInterval = 25;
uint32_t g_polling_interval_ms_for_testing = 0;
} // namespace
-MemoryDumpScheduler::MemoryDumpScheduler(
- MemoryDumpManager* mdm,
- scoped_refptr<SingleThreadTaskRunner> polling_task_runner)
- : mdm_(mdm), polling_state_(polling_task_runner) {}
+// 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(!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;
+ 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_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;
+ 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;
+ 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);
+ 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::NotifyPeriodicTriggerSupported() {
- if (!periodic_state_.is_configured || periodic_state_.timer.IsRunning())
+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_->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(
+ periodic_state_->dump_count = 0;
+ periodic_state_->timer.Start(
FROM_HERE,
- TimeDelta::FromMilliseconds(periodic_state_.min_timer_period_ms),
+ TimeDelta::FromMilliseconds(periodic_state_->min_timer_period_ms),
Bind(&MemoryDumpScheduler::RequestPeriodicGlobalDump, Unretained(this)));
}
-void MemoryDumpScheduler::NotifyPollingSupported() {
- if (polling_state_.current_state != PollingTriggerState::CONFIGURED)
+void MemoryDumpScheduler::EnablePollingIfNeeded() {
+ DCHECK(is_setup_);
+ if (polling_state_->current_state != PollingTriggerState::CONFIGURED)
return;
- polling_state_.current_state = PollingTriggerState::ENABLED;
- polling_state_.ResetTotals();
+ polling_state_->current_state = PollingTriggerState::ENABLED;
+ polling_state_->ResetTotals();
- polling_state_.polling_task_runner->PostTask(
+ polling_task_runner_->PostTask(
FROM_HERE,
Bind(&MemoryDumpScheduler::PollMemoryOnPollingThread, Unretained(this)));
}
void MemoryDumpScheduler::NotifyDumpTriggered() {
- if (polling_state_.polling_task_runner &&
- polling_state_.polling_task_runner->RunsTasksOnCurrentThread()) {
- polling_state_.polling_task_runner->PostTask(
+ if (polling_task_runner_ &&
+ !polling_task_runner_->RunsTasksOnCurrentThread()) {
+ polling_task_runner_->PostTask(
FROM_HERE,
Bind(&MemoryDumpScheduler::NotifyDumpTriggered, Unretained(this)));
return;
}
- if (polling_state_.current_state != PollingTriggerState::ENABLED)
+
+ if (!polling_state_ ||
+ polling_state_->current_state != PollingTriggerState::ENABLED) {
return;
+ }
- polling_state_.ResetTotals();
+ polling_state_->ResetTotals();
}
void MemoryDumpScheduler::DisableAllTriggers() {
- if (periodic_state_.timer.IsRunning())
- periodic_state_.timer.Stop();
- DisablePolling();
-}
+ if (periodic_state_) {
+ if (periodic_state_->timer.IsRunning())
+ periodic_state_->timer.Stop();
+ periodic_state_.reset();
+ }
-void MemoryDumpScheduler::DisablePolling() {
- if (polling_state_.polling_task_runner->RunsTasksOnCurrentThread()) {
- if (polling_state_.polling_task_runner->PostTask(
- FROM_HERE,
- Bind(&MemoryDumpScheduler::DisablePolling, Unretained(this))))
- return;
+ if (polling_task_runner_) {
+ DCHECK(polling_state_);
+ polling_task_runner_->PostTask(
+ FROM_HERE, Bind(&MemoryDumpScheduler::DisablePollingOnPollingThread,
+ Unretained(this)));
+ polling_task_runner_ = nullptr;
}
- polling_state_.current_state = PollingTriggerState::DISABLED;
- polling_state_.polling_task_runner = nullptr;
+ is_setup_ = false;
+}
+
+void MemoryDumpScheduler::DisablePollingOnPollingThread() {
+ polling_state_->current_state = PollingTriggerState::DISABLED;
+ polling_state_.reset();
}
// static
@@ -131,30 +154,30 @@ void MemoryDumpScheduler::SetPollingIntervalForTesting(uint32_t interval) {
}
bool MemoryDumpScheduler::IsPeriodicTimerRunningForTesting() {
- return periodic_state_.timer.IsRunning();
+ 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)
+ 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)
+ 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;
+ ++periodic_state_->dump_count;
mdm_->RequestGlobalDump(MemoryDumpType::PERIODIC_INTERVAL, level_of_detail);
}
void MemoryDumpScheduler::PollMemoryOnPollingThread() {
- if (polling_state_.current_state != PollingTriggerState::ENABLED)
+ if (polling_state_->current_state != PollingTriggerState::ENABLED)
return;
uint64_t polled_memory = 0;
bool res = mdm_->PollFastMemoryTotal(&polled_memory);
DCHECK(res);
- if (polling_state_.level_of_detail == MemoryDumpLevelOfDetail::DETAILED) {
+ if (polling_state_->level_of_detail == MemoryDumpLevelOfDetail::DETAILED) {
TRACE_COUNTER1(MemoryDumpManager::kTraceCategory, "PolledMemoryMB",
polled_memory / 1024 / 1024);
}
@@ -166,14 +189,14 @@ void MemoryDumpScheduler::PollMemoryOnPollingThread() {
polled_memory / 1024 / 1024);
mdm_->RequestGlobalDump(MemoryDumpType::PEAK_MEMORY_USAGE,
- polling_state_.level_of_detail);
+ 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));
+ TimeDelta::FromMilliseconds(polling_state_->polling_interval_ms));
}
bool MemoryDumpScheduler::ShouldTriggerDump(uint64_t current_memory_total) {
@@ -184,52 +207,52 @@ bool MemoryDumpScheduler::ShouldTriggerDump(uint64_t current_memory_total) {
return false;
bool should_dump = false;
- ++polling_state_.num_polls_from_last_dump;
- if (polling_state_.last_dump_memory_total == 0) {
+ ++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) {
+ } 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;
+ current_memory_total - polling_state_->last_dump_memory_total;
should_dump |=
- increase_from_last_dump > polling_state_.memory_increase_threshold;
+ increase_from_last_dump > polling_state_->memory_increase_threshold;
should_dump |= IsCurrentSamplePeak(current_memory_total);
if (should_dump)
- polling_state_.ResetTotals();
+ 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) %
+ 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) {
+ 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] =
+ ->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 += 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 += (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] =
+ ->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.
@@ -256,11 +279,9 @@ MemoryDumpScheduler::PeriodicTriggerState::~PeriodicTriggerState() {
DCHECK(!timer.IsRunning());
}
-MemoryDumpScheduler::PollingTriggerState::PollingTriggerState(
- scoped_refptr<SingleThreadTaskRunner> polling_task_runner)
+MemoryDumpScheduler::PollingTriggerState::PollingTriggerState()
: current_state(DISABLED),
level_of_detail(MemoryDumpLevelOfDetail::FIRST),
- polling_task_runner(polling_task_runner),
polling_interval_ms(g_polling_interval_ms_for_testing
? g_polling_interval_ms_for_testing
: kMemoryTotalsPollingInterval),
@@ -270,9 +291,7 @@ MemoryDumpScheduler::PollingTriggerState::PollingTriggerState(
memory_increase_threshold(0),
last_memory_totals_kb_index(0) {}
-MemoryDumpScheduler::PollingTriggerState::~PollingTriggerState() {
- DCHECK(!polling_task_runner);
-}
+MemoryDumpScheduler::PollingTriggerState::~PollingTriggerState() {}
void MemoryDumpScheduler::PollingTriggerState::ResetTotals() {
if (!memory_increase_threshold) {
@@ -282,8 +301,11 @@ void MemoryDumpScheduler::PollingTriggerState::ResetTotals() {
// Set threshold to 1% of total system memory.
SystemMemoryInfoKB meminfo;
bool res = GetSystemMemoryInfo(&meminfo);
- if (res)
- memory_increase_threshold = (meminfo.total / 100) * 1024;
+ if (res) {
+ memory_increase_threshold =
+ (static_cast<int64_t>(meminfo.total) / 100) * 1024;
+ }
+ DCHECK_GT(memory_increase_threshold, 0u);
#endif
}
diff --git a/base/trace_event/memory_dump_scheduler.h b/base/trace_event/memory_dump_scheduler.h
index fd21fce834..ab8441bc20 100644
--- a/base/trace_event/memory_dump_scheduler.h
+++ b/base/trace_event/memory_dump_scheduler.h
@@ -5,6 +5,8 @@
#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"
@@ -18,42 +20,50 @@ namespace trace_event {
class MemoryDumpManager;
-// Schedules global dump requests based on the triggers added.
+// 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:
- MemoryDumpScheduler(
- MemoryDumpManager* mdm_,
- scoped_refptr<SingleThreadTaskRunner> polling_task_runner);
- ~MemoryDumpScheduler();
+ 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.
+ // interval specified. NOT thread safe.
void AddTrigger(MemoryDumpType trigger_type,
MemoryDumpLevelOfDetail level_of_detail,
uint32_t min_time_between_dumps_ms);
- // Starts periodic dumps.
- void NotifyPeriodicTriggerSupported();
+ // Starts periodic dumps. NOT thread safe and triggers must be added before
+ // enabling.
+ void EnablePeriodicTriggerIfNeeded();
- // Starts polling memory total.
- void NotifyPollingSupported();
+ // 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.
+ // dumps. NOT thread safe.
void NotifyDumpTriggered();
- // Disables all triggers.
+ // 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 PeriodicTriggerState {
+ struct BASE_EXPORT PeriodicTriggerState {
PeriodicTriggerState();
~PeriodicTriggerState();
@@ -71,7 +81,7 @@ class BASE_EXPORT MemoryDumpScheduler {
DISALLOW_COPY_AND_ASSIGN(PeriodicTriggerState);
};
- struct PollingTriggerState {
+ struct BASE_EXPORT PollingTriggerState {
enum State {
CONFIGURED, // Polling trigger was added.
ENABLED, // Polling is running.
@@ -80,8 +90,7 @@ class BASE_EXPORT MemoryDumpScheduler {
static const uint32_t kMaxNumMemorySamples = 50;
- explicit PollingTriggerState(
- scoped_refptr<SingleThreadTaskRunner> polling_task_runner);
+ PollingTriggerState();
~PollingTriggerState();
// Helper to clear the tracked memory totals and poll count from last dump.
@@ -90,7 +99,6 @@ class BASE_EXPORT MemoryDumpScheduler {
State current_state;
MemoryDumpLevelOfDetail level_of_detail;
- scoped_refptr<SingleThreadTaskRunner> polling_task_runner;
uint32_t polling_interval_ms;
// Minimum numer of polls after the last dump at which next dump can be
@@ -106,8 +114,11 @@ class BASE_EXPORT MemoryDumpScheduler {
DISALLOW_COPY_AND_ASSIGN(PollingTriggerState);
};
- // Helper to set polling disabled on the polling thread.
- void DisablePolling();
+ MemoryDumpScheduler();
+ ~MemoryDumpScheduler();
+
+ // Helper to set polling disabled.
+ void DisablePollingOnPollingThread();
// Periodically called by the timer.
void RequestPeriodicGlobalDump();
@@ -129,8 +140,19 @@ class BASE_EXPORT MemoryDumpScheduler {
MemoryDumpManager* mdm_;
- PeriodicTriggerState periodic_state_;
- PollingTriggerState polling_state_;
+ // 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);
};
diff --git a/base/trace_event/memory_dump_scheduler_unittest.cc b/base/trace_event/memory_dump_scheduler_unittest.cc
new file mode 100644
index 0000000000..9af2a3b430
--- /dev/null
+++ b/base/trace_event/memory_dump_scheduler_unittest.cc
@@ -0,0 +1,101 @@
+// 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_infra_background_whitelist.cc b/base/trace_event/memory_infra_background_whitelist.cc
index ae74322040..746068a7b1 100644
--- a/base/trace_event/memory_infra_background_whitelist.cc
+++ b/base/trace_event/memory_infra_background_whitelist.cc
@@ -69,10 +69,70 @@ const char* const kAllocatorDumpNameWhitelist[] = {
"net/http_network_session_0x?/stream_factory",
"net/sdch_manager_0x?",
"net/ssl_session_cache",
- "net/url_request_context_0x?",
- "net/url_request_context_0x?/http_cache",
- "net/url_request_context_0x?/http_network_session",
- "net/url_request_context_0x?/sdch_manager",
+ "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",
diff --git a/base/trace_event/trace_config.cc b/base/trace_event/trace_config.cc
index 36de107bf8..3df09992b1 100644
--- a/base/trace_event/trace_config.cc
+++ b/base/trace_event/trace_config.cc
@@ -11,11 +11,7 @@
#include "base/json/json_reader.h"
#include "base/json/json_writer.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/memory_dump_manager.h"
#include "base/trace_event/memory_dump_request_args.h"
#include "base/trace_event/trace_event.h"
@@ -37,11 +33,6 @@ const char kEnableArgumentFilter[] = "enable-argument-filter";
const char kRecordModeParam[] = "record_mode";
const char kEnableSystraceParam[] = "enable_systrace";
const char kEnableArgumentFilterParam[] = "enable_argument_filter";
-const char kIncludedCategoriesParam[] = "included_categories";
-const char kExcludedCategoriesParam[] = "excluded_categories";
-const char kSyntheticDelaysParam[] = "synthetic_delays";
-
-const char kSyntheticDelayCategoryFilterPrefix[] = "DELAY(";
// String parameters that is used to parse memory dump config in trace config
// string.
@@ -148,27 +139,36 @@ TraceConfig::EventFilterConfig& TraceConfig::EventFilterConfig::operator=(
return *this;
predicate_name_ = rhs.predicate_name_;
- included_categories_ = rhs.included_categories_;
- excluded_categories_ = rhs.excluded_categories_;
+ category_filter_ = rhs.category_filter_;
+
if (rhs.args_)
args_ = rhs.args_->CreateDeepCopy();
return *this;
}
-void TraceConfig::EventFilterConfig::AddIncludedCategory(
- const std::string& category) {
- included_categories_.push_back(category);
+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::AddExcludedCategory(
- const std::string& category) {
- excluded_categories_.push_back(category);
+void TraceConfig::EventFilterConfig::SetCategoryFilter(
+ const TraceConfigCategoryFilter& category_filter) {
+ category_filter_ = category_filter;
}
-void TraceConfig::EventFilterConfig::SetArgs(
- std::unique_ptr<base::DictionaryValue> args) {
- args_ = std::move(args);
+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(
@@ -187,26 +187,7 @@ bool TraceConfig::EventFilterConfig::GetArgAsSet(
bool TraceConfig::EventFilterConfig::IsCategoryGroupEnabled(
const char* category_group_name) const {
- CStringTokenizer category_group_tokens(
- category_group_name, category_group_name + strlen(category_group_name),
- ",");
- while (category_group_tokens.GetNext()) {
- std::string category_group_token = category_group_tokens.token();
-
- for (const auto& excluded_category : excluded_categories_) {
- if (base::MatchPattern(category_group_token, excluded_category)) {
- return false;
- }
- }
-
- for (const auto& included_category : included_categories_) {
- if (base::MatchPattern(category_group_token, included_category)) {
- return true;
- }
- }
- }
-
- return false;
+ return category_filter_.IsCategoryGroupEnabled(category_group_name);
}
TraceConfig::TraceConfig() {
@@ -255,11 +236,8 @@ 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_),
- included_categories_(tc.included_categories_),
- disabled_categories_(tc.disabled_categories_),
- excluded_categories_(tc.excluded_categories_),
- synthetic_delays_(tc.synthetic_delays_),
event_filters_(tc.event_filters_) {}
TraceConfig::~TraceConfig() {
@@ -272,17 +250,14 @@ TraceConfig& TraceConfig::operator=(const TraceConfig& rhs) {
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_;
- included_categories_ = rhs.included_categories_;
- disabled_categories_ = rhs.disabled_categories_;
- excluded_categories_ = rhs.excluded_categories_;
- synthetic_delays_ = rhs.synthetic_delays_;
event_filters_ = rhs.event_filters_;
return *this;
}
const TraceConfig::StringList& TraceConfig::GetSyntheticDelayValues() const {
- return synthetic_delays_;
+ return category_filter_.synthetic_delays();
}
std::string TraceConfig::ToString() const {
@@ -298,69 +273,14 @@ TraceConfig::AsConvertableToTraceFormat() const {
}
std::string TraceConfig::ToCategoryFilterString() 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;
+ return category_filter_.ToFilterString();
}
bool TraceConfig::IsCategoryGroupEnabled(
const char* category_group_name) const {
// TraceLog should call this method only as part of enabling/disabling
// categories.
-
- bool had_enabled_by_default = false;
- DCHECK(category_group_name);
- std::string category_group_name_str = category_group_name;
- StringTokenizer category_group_tokens(category_group_name_str, ",");
- while (category_group_tokens.GetNext()) {
- std::string category_group_token = category_group_tokens.token();
- // Don't allow empty tokens, nor tokens with leading or trailing space.
- DCHECK(!TraceConfig::IsEmptyOrContainsLeadingOrTrailingWhitespace(
- category_group_token))
- << "Disallowed category string";
- if (IsCategoryEnabled(category_group_token.c_str()))
- 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()) {
- std::string category_group_token = category_group_tokens.token();
- 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();
+ return category_filter_.IsCategoryGroupEnabled(category_group_name);
}
void TraceConfig::Merge(const TraceConfig& config) {
@@ -371,28 +291,10 @@ void TraceConfig::Merge(const TraceConfig& config) {
<< "set of options.";
}
- // 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 (HasIncludedPatterns() && config.HasIncludedPatterns()) {
- included_categories_.insert(included_categories_.end(),
- config.included_categories_.begin(),
- config.included_categories_.end());
- } else {
- included_categories_.clear();
- }
+ category_filter_.Merge(config.category_filter_);
memory_dump_config_.Merge(config.memory_dump_config_);
- 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());
event_filters_.insert(event_filters_.end(), config.event_filters().begin(),
config.event_filters().end());
}
@@ -401,10 +303,7 @@ void TraceConfig::Clear() {
record_mode_ = RECORD_UNTIL_FULL;
enable_systrace_ = false;
enable_argument_filter_ = false;
- included_categories_.clear();
- disabled_categories_.clear();
- excluded_categories_.clear();
- synthetic_delays_.clear();
+ category_filter_.Clear();
memory_dump_config_.Clear();
event_filters_.clear();
}
@@ -435,19 +334,13 @@ void TraceConfig::InitializeFromConfigDict(const DictionaryValue& dict) {
enable_argument_filter_ =
dict.GetBoolean(kEnableArgumentFilterParam, &val) ? val : false;
- 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);
+ category_filter_.InitializeFromConfigDict(dict);
const base::ListValue* category_event_filters = nullptr;
if (dict.GetList(kEventFiltersParam, &category_event_filters))
SetEventFiltersFromConfigList(*category_event_filters);
- if (IsCategoryEnabled(MemoryDumpManager::kTraceCategory)) {
+ 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;
@@ -468,37 +361,8 @@ void TraceConfig::InitializeFromConfigString(StringPiece config_string) {
void TraceConfig::InitializeFromStrings(StringPiece category_filter_string,
StringPiece trace_options_string) {
- if (!category_filter_string.empty()) {
- std::vector<std::string> split = SplitString(
- category_filter_string, ",", TRIM_WHITESPACE, SPLIT_WANT_ALL);
- for (const std::string& 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() == ')') {
- std::string 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);
- }
- } else if (category.front() == '-') {
- // Excluded categories start with '-'.
- // Remove '-' from category string.
- excluded_categories_.push_back(category.substr(1));
- } else 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);
- }
- }
- }
+ if (!category_filter_string.empty())
+ category_filter_.InitializeFromString(category_filter_string);
record_mode_ = RECORD_UNTIL_FULL;
enable_systrace_ = false;
@@ -523,64 +387,11 @@ void TraceConfig::InitializeFromStrings(StringPiece category_filter_string,
}
}
- if (IsCategoryEnabled(MemoryDumpManager::kTraceCategory)) {
+ if (category_filter_.IsCategoryEnabled(MemoryDumpManager::kTraceCategory)) {
SetDefaultMemoryDumpConfig();
}
}
-void TraceConfig::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 TraceConfig::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 TraceConfig::SetSyntheticDelaysFromList(const ListValue& list) {
- synthetic_delays_.clear();
- 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 TraceConfig::AddCategoryToDict(DictionaryValue* dict,
- const char* param,
- const StringList& categories) 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 TraceConfig::SetMemoryDumpConfigFromConfigDict(
const DictionaryValue& memory_dump_config) {
// Set allowed dump modes.
@@ -673,29 +484,7 @@ void TraceConfig::SetEventFiltersFromConfigList(
<< "Invalid predicate name in category event filter.";
EventFilterConfig new_config(predicate_name);
- const base::ListValue* included_list = nullptr;
- CHECK(event_filter->GetList(kIncludedCategoriesParam, &included_list))
- << "Missing included_categories in category event filter.";
-
- for (size_t i = 0; i < included_list->GetSize(); ++i) {
- std::string category;
- if (included_list->GetString(i, &category))
- new_config.AddIncludedCategory(category);
- }
-
- const base::ListValue* excluded_list = nullptr;
- if (event_filter->GetList(kExcludedCategoriesParam, &excluded_list)) {
- for (size_t i = 0; i < excluded_list->GetSize(); ++i) {
- std::string category;
- if (excluded_list->GetString(i, &category))
- new_config.AddExcludedCategory(category);
- }
- }
-
- const base::DictionaryValue* args_dict = nullptr;
- if (event_filter->GetDictionary(kFilterArgsParam, &args_dict))
- new_config.SetArgs(args_dict->CreateDeepCopy());
-
+ new_config.InitializeFromConfigDict(event_filter);
event_filters_.push_back(new_config);
}
}
@@ -722,50 +511,20 @@ std::unique_ptr<DictionaryValue> TraceConfig::ToDict() const {
dict->SetBoolean(kEnableSystraceParam, enable_systrace_);
dict->SetBoolean(kEnableArgumentFilterParam, enable_argument_filter_);
- StringList categories(included_categories_);
- categories.insert(categories.end(),
- disabled_categories_.begin(),
- disabled_categories_.end());
- AddCategoryToDict(dict.get(), kIncludedCategoriesParam, categories);
- AddCategoryToDict(dict.get(), kExcludedCategoriesParam, excluded_categories_);
- AddCategoryToDict(dict.get(), kSyntheticDelaysParam, synthetic_delays_);
+ 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_dict->SetString(kFilterPredicateParam, filter.predicate_name());
-
- std::unique_ptr<base::ListValue> included_categories_list(
- new base::ListValue());
- for (const std::string& included_category : filter.included_categories())
- included_categories_list->AppendString(included_category);
-
- filter_dict->Set(kIncludedCategoriesParam,
- std::move(included_categories_list));
-
- if (!filter.excluded_categories().empty()) {
- std::unique_ptr<base::ListValue> excluded_categories_list(
- new base::ListValue());
- for (const std::string& excluded_category :
- filter.excluded_categories())
- excluded_categories_list->AppendString(excluded_category);
-
- filter_dict->Set(kExcludedCategoriesParam,
- std::move(excluded_categories_list));
- }
-
- if (filter.filter_args())
- filter_dict->Set(kFilterArgsParam,
- filter.filter_args()->CreateDeepCopy());
-
+ filter.ToDict(filter_dict.get());
filter_list->Append(std::move(filter_dict));
}
dict->Set(kEventFiltersParam, std::move(filter_list));
}
- if (IsCategoryEnabled(MemoryDumpManager::kTraceCategory)) {
+ 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));
@@ -829,59 +588,5 @@ std::string TraceConfig::ToTraceOptionsString() const {
return ret;
}
-void TraceConfig::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 TraceConfig::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;
- }
-}
-
-bool TraceConfig::IsCategoryEnabled(const char* 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;
-}
-
-bool TraceConfig::IsEmptyOrContainsLeadingOrTrailingWhitespace(
- StringPiece str) {
- return str.empty() || str.front() == ' ' || str.back() == ' ';
-}
-
-bool TraceConfig::HasIncludedPatterns() const {
- return !included_categories_.empty();
-}
-
} // namespace trace_event
} // namespace base
diff --git a/base/trace_event/trace_config.h b/base/trace_event/trace_config.h
index 717c261316..29edc9a8ec 100644
--- a/base/trace_event/trace_config.h
+++ b/base/trace_event/trace_config.h
@@ -17,6 +17,7 @@
#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 {
@@ -94,26 +95,25 @@ class BASE_EXPORT TraceConfig {
EventFilterConfig& operator=(const EventFilterConfig& rhs);
- void AddIncludedCategory(const std::string& category);
- void AddExcludedCategory(const std::string& category);
- void SetArgs(std::unique_ptr<base::DictionaryValue> args);
+ 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 char* category_group_name) const;
const std::string& predicate_name() const { return predicate_name_; }
base::DictionaryValue* filter_args() const { return args_.get(); }
- const StringList& included_categories() const {
- return included_categories_;
- }
- const StringList& excluded_categories() const {
- return excluded_categories_;
+ const TraceConfigCategoryFilter& category_filter() const {
+ return category_filter_;
}
private:
std::string predicate_name_;
- StringList included_categories_;
- StringList excluded_categories_;
+ TraceConfigCategoryFilter category_filter_;
std::unique_ptr<base::DictionaryValue> args_;
};
typedef std::vector<EventFilterConfig> EventFilters;
@@ -241,6 +241,10 @@ class BASE_EXPORT TraceConfig {
// 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_;
}
@@ -254,15 +258,6 @@ class BASE_EXPORT TraceConfig {
FRIEND_TEST_ALL_PREFIXES(TraceConfigTest, TraceConfigFromValidLegacyFormat);
FRIEND_TEST_ALL_PREFIXES(TraceConfigTest,
TraceConfigFromInvalidLegacyStrings);
- FRIEND_TEST_ALL_PREFIXES(TraceConfigTest, TraceConfigFromValidString);
- FRIEND_TEST_ALL_PREFIXES(TraceConfigTest, TraceConfigFromInvalidString);
- FRIEND_TEST_ALL_PREFIXES(TraceConfigTest,
- IsEmptyOrContainsLeadingOrTrailingWhitespace);
- FRIEND_TEST_ALL_PREFIXES(TraceConfigTest, TraceConfigFromMemoryConfigString);
- FRIEND_TEST_ALL_PREFIXES(TraceConfigTest, LegacyStringToMemoryDumpConfig);
- FRIEND_TEST_ALL_PREFIXES(TraceConfigTest, EmptyMemoryDumpConfigTest);
- FRIEND_TEST_ALL_PREFIXES(TraceConfigTest,
- EmptyAndAsteriskCategoryFilterString);
// The default trace config, used when none is provided.
// Allows all non-disabled-by-default categories through, except if they end
@@ -279,13 +274,6 @@ class BASE_EXPORT TraceConfig {
void InitializeFromStrings(StringPiece category_filter_string,
StringPiece trace_options_string);
- void SetCategoriesFromIncludedList(const ListValue& included_list);
- void SetCategoriesFromExcludedList(const ListValue& excluded_list);
- void SetSyntheticDelaysFromList(const ListValue& list);
- void AddCategoryToDict(DictionaryValue* dict,
- const char* param,
- const StringList& categories) const;
-
void SetMemoryDumpConfigFromConfigDict(
const DictionaryValue& memory_dump_config);
void SetDefaultMemoryDumpConfig();
@@ -295,32 +283,14 @@ class BASE_EXPORT TraceConfig {
std::string ToTraceOptionsString() const;
- void WriteCategoryFilterString(const StringList& values,
- std::string* out,
- bool included) const;
- void WriteCategoryFilterString(const StringList& delays,
- std::string* out) 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 char* category_name) const;
-
- static bool IsEmptyOrContainsLeadingOrTrailingWhitespace(StringPiece str);
-
- bool HasIncludedPatterns() const;
-
TraceRecordMode record_mode_;
bool enable_systrace_ : 1;
bool enable_argument_filter_ : 1;
+ TraceConfigCategoryFilter category_filter_;
+
MemoryDumpConfig memory_dump_config_;
- StringList included_categories_;
- StringList disabled_categories_;
- StringList excluded_categories_;
- StringList synthetic_delays_;
EventFilters event_filters_;
};
diff --git a/base/trace_event/trace_config_category_filter.cc b/base/trace_event/trace_config_category_filter.cc
new file mode 100644
index 0000000000..dc30e0ea99
--- /dev/null
+++ b/base/trace_event/trace_config_category_filter.cc
@@ -0,0 +1,298 @@
+// 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<std::string> split =
+ SplitString(category_filter_string, ",", TRIM_WHITESPACE, SPLIT_WANT_ALL);
+ for (const std::string& 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() == ')') {
+ std::string 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);
+ }
+ } else if (category.front() == '-') {
+ // Excluded categories start with '-'.
+ // Remove '-' from category string.
+ excluded_categories_.push_back(category.substr(1));
+ } else 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::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 char* category_group_name) const {
+ bool had_enabled_by_default = false;
+ DCHECK(category_group_name);
+ std::string category_group_name_str = category_group_name;
+ StringTokenizer category_group_tokens(category_group_name_str, ",");
+ while (category_group_tokens.GetNext()) {
+ std::string category_group_token = category_group_tokens.token();
+ // 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.c_str()))
+ 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()) {
+ std::string category_group_token = category_group_tokens.token();
+ 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 char* 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
new file mode 100644
index 0000000000..df8c3a5b2a
--- /dev/null
+++ b/base/trace_event/trace_config_category_filter.h
@@ -0,0 +1,86 @@
+// 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 char* 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 char* 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_unittest.cc b/base/trace_event/trace_config_unittest.cc
index 74aa7bdc63..a856c27192 100644
--- a/base/trace_event/trace_config_unittest.cc
+++ b/base/trace_event/trace_config_unittest.cc
@@ -304,10 +304,12 @@ TEST(TraceConfigTest, EmptyAndAsteriskCategoryFilterString) {
CheckDefaultTraceConfigBehavior(tc_asterisk);
// They differ only for internal checking.
- EXPECT_FALSE(tc_empty.IsCategoryEnabled("Category1"));
- EXPECT_FALSE(tc_empty.IsCategoryEnabled("not-excluded-category"));
- EXPECT_TRUE(tc_asterisk.IsCategoryEnabled("Category1"));
- EXPECT_TRUE(tc_asterisk.IsCategoryEnabled("not-excluded-category"));
+ 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) {
@@ -402,13 +404,15 @@ TEST(TraceConfigTest, TraceConfigFromValidString) {
"-exc_pattern*,DELAY(test.Delay1;16),DELAY(test.Delay2;32)",
tc.ToCategoryFilterString().c_str());
- EXPECT_TRUE(tc.IsCategoryEnabled("included"));
- EXPECT_TRUE(tc.IsCategoryEnabled("inc_pattern_category"));
- EXPECT_TRUE(tc.IsCategoryEnabled("disabled-by-default-cc"));
- EXPECT_FALSE(tc.IsCategoryEnabled("excluded"));
- EXPECT_FALSE(tc.IsCategoryEnabled("exc_pattern_category"));
- EXPECT_FALSE(tc.IsCategoryEnabled("disabled-by-default-others"));
- EXPECT_FALSE(tc.IsCategoryEnabled("not-excluded-nor-included"));
+ 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"));
@@ -431,10 +435,12 @@ TEST(TraceConfigTest, TraceConfigFromValidString) {
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.included_categories().size());
- EXPECT_STREQ("*", event_filter.included_categories()[0].c_str());
- EXPECT_EQ(1u, event_filter.excluded_categories().size());
- EXPECT_STREQ("unfiltered_cat", event_filter.excluded_categories()[0].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;
@@ -449,8 +455,10 @@ TEST(TraceConfigTest, TraceConfigFromValidString) {
const char config_string_2[] = "{\"included_categories\":[\"*\"]}";
TraceConfig tc2(config_string_2);
- EXPECT_TRUE(tc2.IsCategoryEnabled("non-disabled-by-default-pattern"));
- EXPECT_FALSE(tc2.IsCategoryEnabled("disabled-by-default-pattern"));
+ 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"));
@@ -538,8 +546,9 @@ TEST(TraceConfigTest, TraceConfigFromInvalidString) {
"\"excluded_categories\":[\"category\",\"disabled-by-default-pattern\"]"
"}";
tc = TraceConfig(invalid_config_string_2);
- EXPECT_TRUE(tc.IsCategoryEnabled("category"));
- EXPECT_TRUE(tc.IsCategoryEnabled("disabled-by-default-pattern"));
+ 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"));
}
@@ -591,27 +600,25 @@ TEST(TraceConfigTest, IsCategoryGroupEnabled) {
EXPECT_FALSE(tc.IsCategoryGroupEnabled("excluded,disabled-by-default-cc"));
}
-TEST(TraceConfigTest, IsEmptyOrContainsLeadingOrTrailingWhitespace) {
- // Test that IsEmptyOrContainsLeadingOrTrailingWhitespace 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_TRUE(TraceConfig::IsEmptyOrContainsLeadingOrTrailingWhitespace(
- " bad_category "));
- EXPECT_TRUE(TraceConfig::IsEmptyOrContainsLeadingOrTrailingWhitespace(
- " bad_category"));
- EXPECT_TRUE(TraceConfig::IsEmptyOrContainsLeadingOrTrailingWhitespace(
- "bad_category "));
- EXPECT_TRUE(TraceConfig::IsEmptyOrContainsLeadingOrTrailingWhitespace(
- " bad_category"));
- EXPECT_TRUE(TraceConfig::IsEmptyOrContainsLeadingOrTrailingWhitespace(
- "bad_category "));
- EXPECT_TRUE(TraceConfig::IsEmptyOrContainsLeadingOrTrailingWhitespace(
- " bad_category "));
- EXPECT_TRUE(TraceConfig::IsEmptyOrContainsLeadingOrTrailingWhitespace(
- ""));
- EXPECT_FALSE(TraceConfig::IsEmptyOrContainsLeadingOrTrailingWhitespace(
- "good_category"));
+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) {
@@ -637,20 +644,20 @@ TEST(TraceConfigTest, TraceConfigFromMemoryConfigString) {
EXPECT_EQ(tc_str1, tc2.ToString());
EXPECT_TRUE(tc1.IsCategoryGroupEnabled(MemoryDumpManager::kTraceCategory));
- ASSERT_EQ(2u, tc1.memory_dump_config_.triggers.size());
+ ASSERT_EQ(2u, tc1.memory_dump_config().triggers.size());
EXPECT_EQ(200u,
- tc1.memory_dump_config_.triggers[0].min_time_between_dumps_ms);
+ tc1.memory_dump_config().triggers[0].min_time_between_dumps_ms);
EXPECT_EQ(MemoryDumpLevelOfDetail::LIGHT,
- tc1.memory_dump_config_.triggers[0].level_of_detail);
+ tc1.memory_dump_config().triggers[0].level_of_detail);
EXPECT_EQ(2000u,
- tc1.memory_dump_config_.triggers[1].min_time_between_dumps_ms);
+ tc1.memory_dump_config().triggers[1].min_time_between_dumps_ms);
EXPECT_EQ(MemoryDumpLevelOfDetail::DETAILED,
- tc1.memory_dump_config_.triggers[1].level_of_detail);
+ tc1.memory_dump_config().triggers[1].level_of_detail);
EXPECT_EQ(
2048u,
- tc1.memory_dump_config_.heap_profiler_options.breakdown_threshold_bytes);
+ tc1.memory_dump_config().heap_profiler_options.breakdown_threshold_bytes);
std::string tc_str3 =
TraceConfigMemoryTestUtil::GetTraceConfig_BackgroundTrigger(
@@ -658,20 +665,20 @@ TEST(TraceConfigTest, TraceConfigFromMemoryConfigString) {
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);
+ 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);
+ 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);
+ 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);
+ tc4.memory_dump_config().triggers[0].level_of_detail);
}
TEST(TraceConfigTest, EmptyMemoryDumpConfigTest) {
@@ -679,22 +686,22 @@ TEST(TraceConfigTest, EmptyMemoryDumpConfigTest) {
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);
+ 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);
+ 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
diff --git a/base/trace_event/trace_event_unittest.cc b/base/trace_event/trace_event_unittest.cc
index 82a552aa4e..85e1e16312 100644
--- a/base/trace_event/trace_event_unittest.cc
+++ b/base/trace_event/trace_event_unittest.cc
@@ -3088,11 +3088,15 @@ TEST_F(TraceEventTestFixture, EventFiltering) {
"{"
" \"included_categories\": ["
" \"filtered_cat\","
- " \"unfiltered_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\"]"
+ " \"included_categories\": ["
+ " \"filtered_cat\","
+ " \"" TRACE_DISABLED_BY_DEFAULT("filtered_cat") "\"]"
" }"
" "
" ]"
@@ -3111,12 +3115,15 @@ TEST_F(TraceEventTestFixture, EventFiltering) {
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(3u, filter_hits_counter.filter_trace_event_hit_count);
+ EXPECT_EQ(4u, filter_hits_counter.filter_trace_event_hit_count);
EXPECT_EQ(1u, filter_hits_counter.end_event_hit_count);
}
@@ -3125,12 +3132,14 @@ TEST_F(TraceEventTestFixture, EventWhitelistFiltering) {
"{"
" \"included_categories\": ["
" \"filtered_cat\","
- " \"unfiltered_cat\"],"
+ " \"unfiltered_cat\","
+ " \"" TRACE_DISABLED_BY_DEFAULT("filtered_cat") "\"],"
" \"event_filters\": ["
" {"
" \"filter_predicate\": \"%s\", "
- " \"included_categories\": [\"*\"], "
- " \"excluded_categories\": [\"unfiltered_cat\"], "
+ " \"included_categories\": ["
+ " \"filtered_cat\","
+ " \"" TRACE_DISABLED_BY_DEFAULT("*") "\"], "
" \"filter_args\": {"
" \"event_name_whitelist\": [\"a snake\", \"a dog\"]"
" }"
@@ -3148,12 +3157,16 @@ TEST_F(TraceEventTestFixture, EventWhitelistFiltering) {
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) {
@@ -3161,12 +3174,16 @@ TEST_F(TraceEventTestFixture, HeapProfilerFiltering) {
"{"
" \"included_categories\": ["
" \"filtered_cat\","
- " \"unfiltered_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\": [\"*\"]"
+ " \"included_categories\": ["
+ " \"*\","
+ " \"" TRACE_DISABLED_BY_DEFAULT("filtered_cat") "\"]"
" }"
" ]"
"}",
@@ -3180,6 +3197,8 @@ TEST_F(TraceEventTestFixture, HeapProfilerFiltering) {
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();
@@ -3187,6 +3206,8 @@ TEST_F(TraceEventTestFixture, HeapProfilerFiltering) {
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) {
diff --git a/base/trace_event/trace_log.cc b/base/trace_event/trace_log.cc
index 10b090ae57..d798a9539b 100644
--- a/base/trace_event/trace_log.cc
+++ b/base/trace_event/trace_log.cc
@@ -19,8 +19,10 @@
#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"
@@ -1509,8 +1511,18 @@ void TraceLog::AddMetadataEventsWhileLocked() {
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<std::string> labels;
+ std::vector<base::StringPiece> labels;
for (const auto& it : process_labels_)
labels.push_back(it.second);
InitializeMetadataEvent(AddEventToThreadSharedChunkWhileLocked(NULL, false),