Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2663)

Unified Diff: base/debug/activity_tracker.h

Issue 1980743002: Track thread activities in order to diagnose hangs. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@readwrite-mmf
Patch Set: track locks and waitable events Created 4 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: base/debug/activity_tracker.h
diff --git a/base/debug/activity_tracker.h b/base/debug/activity_tracker.h
new file mode 100644
index 0000000000000000000000000000000000000000..ffc3b19c6c7ae7bf81e1dc93499b705c8a157709
--- /dev/null
+++ b/base/debug/activity_tracker.h
@@ -0,0 +1,344 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_METRICS_ACTIVITY_TRACKER_H_
+#define BASE_METRICS_ACTIVITY_TRACKER_H_
+
+#include <atomic>
+#include <memory>
+
+#include "base/base_export.h"
+#include "base/feature_list.h"
+#include "base/files/file_path.h"
+#include "base/metrics/persistent_memory_allocator.h"
+#include "base/threading/thread_checker.h"
+#include "base/threading/thread_local_storage.h"
+
+namespace base {
+
+struct PendingTask;
+
+class Lock;
+class MemoryMappedFile;
+class WaitableEvent;
+
+namespace debug {
+
+class ThreadActivityAnalyzer;
+
+#if !defined(OS_NACL) // NACL doesn't support any kind of file access in build.
manzagop (departed) 2016/06/01 21:59:41 Do you need this #if at the callsite in chrome_br
bcwhite 2016/06/02 16:18:16 No because that file is under chrome/ which isn't
+// Enables the global activity tracker according to a field trial setting,
+// using the specified |file| (without extension) for storing information
+// from this run.
+BASE_EXPORT void SetupGlobalActivityTrackerFieldTrial(const FilePath& file);
+#endif // !defined(OS_NACL)
+
+
+// This class manages tracking a stack of activities for a single thread in
+// a persistent manner. However, in order to support an operational mode where
+// another thread is analyzing this data in real-time, atomic operations are
+// used where necessary to guarantee a consistent view from the outside.
+class BASE_EXPORT ThreadActivityTracker {
+ public:
+ enum ActivityType : uint8_t {
+ ACT_TASK,
+ ACT_LOCK,
+ ACT_EVENT,
+ };
+
+ union StackEntryData {
+ struct {
+ uint64_t sequence_id;
+ } task;
+ struct {
+ uint64_t lock_address;
+ } lock;
+ struct {
+ uint64_t event_address;
+ } event;
+
+ static StackEntryData ForTask(uint64_t sequence) {
+ StackEntryData data;
+ data.task.sequence_id = sequence;
+ return data;
+ }
+
+ static StackEntryData ForLock(const void* lock) {
manzagop (departed) 2016/06/01 21:59:41 Add a comment to specificy this is "ForLockAcquisi
bcwhite 2016/06/02 16:18:16 Yeah, lots of comments required everywhere. The s
+ StackEntryData data;
+ data.lock.lock_address = reinterpret_cast<uintptr_t>(lock);
+ return data;
+ }
+
+ static StackEntryData ForEvent(const void* event) {
+ StackEntryData data;
+ data.event.event_address = reinterpret_cast<uintptr_t>(event);
+ return data;
+ }
+ };
+
+ struct StackEntry {
+ int64_t time_ticks;
+ uintptr_t source_address;
+ uint8_t activity_type;
+ StackEntryData data;
+ };
+
+ class BASE_EXPORT ScopedActivity {
+ public:
+ ScopedActivity(ThreadActivityTracker* tracker,
+ const void* source,
+ ActivityType activity,
+ const StackEntryData& data)
+ : tracker_(tracker), source_(source) {
+ if (tracker_)
+ tracker_->PushActivity(source, activity, data);
+ }
+ ~ScopedActivity() {
+ if (tracker_)
+ tracker_->PopActivity(source_);
+ }
+
+ private:
+ ThreadActivityTracker* const tracker_;
+ const void* const source_;
+ };
+
+ // A ThreadActivityTracker runs on top of memory that is managed externally.
+ // It must be large enough for the internal header and a few StackEntry
+ // blocks. See SizeForStackDepth().
+ ThreadActivityTracker(void* base, size_t size);
+ virtual ~ThreadActivityTracker();
+
+ // Indicate that a method of the given (arbitrary) identifier has started.
+ void PushActivity(const void* source,
+ ActivityType activity,
+ const StackEntryData& data);
+
+ // Indicate that a method of the given (arbitrary) identifier has finished.
+ void PopActivity(const void* source);
+
+ // Returns whether the current data is valid or not. It is not valid if
+ // corruption is detected in the header or other data structures. Fetching
+ // a copy of the stack will return nothing if the data is not valid.
+ bool is_valid() { return valid_; }
+
+ // Calculates the memory size required for a given stack depth, including
+ // the internal header structure for the stack.
+ static size_t SizeForStackDepth(int stack_depth);
+
+ private:
+ friend class ThreadActivityAnalyzer;
+ friend class ActivityTrackerTest;
+
+ struct Header;
+
+ Header* const header_;
+ StackEntry* const stack_;
+ const uint32_t stack_slots_;
+
+ bool valid_ = false;
+
+ base::ThreadChecker thread_checker_;
+
+ DISALLOW_COPY_AND_ASSIGN(ThreadActivityTracker);
+};
+
+
+// Be sure not to create multiple Analyzers for given tracker data as parallel
+// operation could lead to inconsistencies from concurrent synchronization with
+// an active tracker.
+class BASE_EXPORT ThreadActivityAnalyzer {
+ public:
+ using StackEntry = ThreadActivityTracker::StackEntry;
+
+ // Creates an analyzer for an existing activity-tracker. The passed tracker
+ // must live at least as long as the created object. The tracker may continue
+ // to be active even with an attached analyzer.
+ explicit ThreadActivityAnalyzer(ThreadActivityTracker* tracker);
+
+ // Creates an anaylzer for a block of memory held within a persistent-memory
+ // |allocator| at the given |reference|. The memory must live at least as
+ // long as the created object. It's permissable for a tracker to remain active
+ // on the memory from this thread, other threads, or even other processes.
+ // The reference must be to an object of type kTypeIdActivityTracker, above.
+ ThreadActivityAnalyzer(PersistentMemoryAllocator* allocator,
+ PersistentMemoryAllocator::Reference reference);
+
+ // Creates an anaylzer for a block of memory currently or previously in-use
+ // by an activity-tracker. The memory must live at least as long as the
+ // created object. It's permissable for a tracker to remain active on the
+ // memory from this thread, other threads, or even other processes.
+ ThreadActivityAnalyzer(void* base, size_t size);
+
+ ~ThreadActivityAnalyzer();
+
+ // Gets a copy of the current stack contents. The return value is the current
+ // depth of the stack which may be greater than the number of StackEntry
+ // records returned. If so, the returned stack has the "base" of the stack
+ // with later entries omitted.
+ uint32_t SnapshotStack(std::vector<StackEntry>* snapshot);
+
+ private:
+ ThreadActivityTracker tracker_;
+
+ DISALLOW_COPY_AND_ASSIGN(ThreadActivityAnalyzer);
+};
+
+
+class BASE_EXPORT GlobalActivityTracker {
+ public:
+ // Type identifiers used when storing in persistent memory so they can be
+ // identified during extraction; the first 4 bytes of the SHA1 of the name
+ // is used as a unique integer. A "version number" is added to the base
+ // so that, if the structure of that object changes, stored older versions
+ // will be safely ignored. These are public so that an external process
+ // can recognize records of this type within an allocator and use them to
+ // create ThreadActivityAnalyzer objects.
+ enum : uint32_t {
+ kTypeIdActivityTracker = 0x5D7381AF + 1, // SHA1(ActivityTracker) v1
+ kTypeIdActivityTrackerFree = 0x3F0272FB, // SHA1(ActivityTrackerFree)
+ };
+
+ class BASE_EXPORT ScopedThreadActivity
+ : public ThreadActivityTracker::ScopedActivity {
+ public:
+ ScopedThreadActivity(const void* source,
+ ThreadActivityTracker::ActivityType activity,
+ const ThreadActivityTracker::StackEntryData& data,
+ bool lock_allowed)
+ : ThreadActivityTracker::ScopedActivity(
+ GetOrCreateTracker(lock_allowed),
+ source,
+ activity,
+ data) {}
+
+ private:
+ static ThreadActivityTracker* GetOrCreateTracker(bool lock_allowed) {
+ GlobalActivityTracker* global_tracker = Get();
+ if (!global_tracker)
+ return nullptr;
+ if (lock_allowed)
+ return global_tracker->GetOrCreateTrackerForCurrentThread();
+ else
+ return global_tracker->GetTrackerForCurrentThread();
+ }
+ };
+
+ ~GlobalActivityTracker();
+
+ static void CreateWithAllocator(
+ std::unique_ptr<PersistentMemoryAllocator> allocator,
+ int stack_depth);
+
+ static void CreateWithLocalMemory(size_t size,
+ uint64_t id,
+ StringPiece name,
+ int stack_depth);
+
+#if !defined(OS_NACL)
+ static void CreateWithFile(const FilePath& file_path,
+ size_t size,
+ uint64_t id,
+ StringPiece name,
+ int stack_depth);
+#endif // !defined(OS_NACL)
+
+ // Gets the global activity-tracker or null if none exists.
+ static GlobalActivityTracker* Get() { return g_tracker_; }
+
+ // Gets the persistent-memory-allocator in which data is stored. Callers
+ // can store additional records here to pass additional information to
+ // the analysis process.
+ PersistentMemoryAllocator* allocator() { return allocator_.get(); }
+
+ // Gets the thread's activity-tracker if it exists. This is inline for
+ // performance reasons. Ownership remains with the global tracker.
+ ThreadActivityTracker* GetTrackerForCurrentThread() {
+ void* tracker = this_thread_tracker_.Get();
+ return reinterpret_cast<ThreadActivityTracker*>(tracker);
+ }
+
+ // Gets the thread's activity-tracker or creates one if none exists. This
+ // is inline for performance reasons. Ownership remains with the global
+ // tracker.
+ ThreadActivityTracker* GetOrCreateTrackerForCurrentThread() {
+ ThreadActivityTracker* tracker = GetTrackerForCurrentThread();
+ if (!tracker)
+ tracker = CreateTrackerForCurrentThread();
+ return tracker;
+ }
+
+ // Creates an activity-tracker for the current thread.
+ ThreadActivityTracker* CreateTrackerForCurrentThread();
+
+ // Releases the activity-tracker for the current thread (for testing only).
+ void ReleaseTrackerForCurrentThreadForTesting();
+
+ private:
+ friend class ActivityTrackerTest;
+
+ enum : int {
+ kMaxThreadCount = 100,
+ };
+
+ class ManagedActivityTracker : public ThreadActivityTracker {
+ public:
+ ManagedActivityTracker(PersistentMemoryAllocator::Reference mem_reference,
+ void* base,
+ size_t size);
+ ~ManagedActivityTracker() override;
+
+ private:
+ const PersistentMemoryAllocator::Reference mem_reference_;
+ void* const mem_base_;
+ };
+
+ GlobalActivityTracker(std::unique_ptr<PersistentMemoryAllocator> allocator,
+ int stack_depth);
+
+ // Returns the memory used by an activity-tracker managed by this class.
+ void ReturnTrackerMemory(ManagedActivityTracker* tracker,
+ PersistentMemoryAllocator::Reference mem_reference,
+ void* mem_base);
+
+ static void OnTLSDestroy(void* value);
+
+ std::unique_ptr<PersistentMemoryAllocator> allocator_;
+ const size_t stack_memory_size_;
+
+ base::ThreadLocalStorage::Slot this_thread_tracker_;
+
+ // These have to be lock-free because lock activity is tracked and causes
+ // re-entry problems.
+ std::atomic<int> thread_tracker_count_;
+ std::atomic<int> available_memories_count_;
+ std::atomic<PersistentMemoryAllocator::Reference>
+ available_memories_[kMaxThreadCount];
+
+ static GlobalActivityTracker* g_tracker_;
+};
+
+
+class BASE_EXPORT ScopedTaskActivity
+ : public GlobalActivityTracker::ScopedThreadActivity {
+ public:
+ ScopedTaskActivity(const base::PendingTask& task);
+};
+
+class BASE_EXPORT ScopedLockActivity
+ : public GlobalActivityTracker::ScopedThreadActivity {
+ public:
+ ScopedLockActivity(const base::internal::LockImpl* lock);
+};
+
+class BASE_EXPORT ScopedEventActivity
+ : public GlobalActivityTracker::ScopedThreadActivity {
+ public:
+ ScopedEventActivity(const base::WaitableEvent* event);
+};
+
+} // namespace debug
+} // namespace base
+
+#endif // BASE_METRICS_ACTIVITY_TRACKER_H_
« no previous file with comments | « base/base.gypi ('k') | base/debug/activity_tracker.cc » ('j') | base/debug/activity_tracker.cc » ('J')

Powered by Google App Engine
This is Rietveld 408576698