| OLD | NEW |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "base/debug/activity_tracker.h" | 5 #include "base/debug/activity_tracker.h" |
| 6 | 6 |
| 7 #include "base/debug/stack_trace.h" | 7 #include "base/debug/stack_trace.h" |
| 8 #include "base/files/file.h" | 8 #include "base/files/file.h" |
| 9 #include "base/files/file_path.h" | 9 #include "base/files/file_path.h" |
| 10 #include "base/files/memory_mapped_file.h" | 10 #include "base/files/memory_mapped_file.h" |
| 11 #include "base/logging.h" | 11 #include "base/logging.h" |
| 12 #include "base/memory/ptr_util.h" | 12 #include "base/memory/ptr_util.h" |
| 13 #include "base/metrics/field_trial.h" | 13 #include "base/metrics/field_trial.h" |
| 14 #include "base/metrics/histogram_macros.h" | 14 #include "base/metrics/histogram_macros.h" |
| 15 #include "base/pending_task.h" | 15 #include "base/pending_task.h" |
| 16 #include "base/process/process.h" | 16 #include "base/process/process.h" |
| 17 #include "base/process/process_handle.h" | 17 #include "base/process/process_handle.h" |
| 18 #include "base/stl_util.h" | 18 #include "base/stl_util.h" |
| 19 #include "base/strings/string_util.h" | 19 #include "base/strings/string_util.h" |
| 20 #include "base/threading/platform_thread.h" | 20 #include "base/threading/platform_thread.h" |
| 21 | 21 |
| 22 namespace base { | 22 namespace base { |
| 23 namespace debug { | 23 namespace debug { |
| 24 | 24 |
| 25 namespace { | 25 namespace { |
| 26 | 26 |
| 27 // A number that identifies the memory as having been initialized. It's | 27 // A number that identifies the memory as having been initialized. It's |
| 28 // arbitrary but happens to be the first 8 bytes of SHA1(ThreadActivityTracker). | 28 // arbitrary but happens to be the first 4 bytes of SHA1(ThreadActivityTracker). |
| 29 // A version number is added on so that major structure changes won't try to | 29 // A version number is added on so that major structure changes won't try to |
| 30 // read an older version (since the cookie won't match). | 30 // read an older version (since the cookie won't match). |
| 31 const uint64_t kHeaderCookie = 0xC0029B240D4A3092ULL + 1; // v1 | 31 const uint32_t kHeaderCookie = 0xC0029B24UL + 2; // v2 |
| 32 | 32 |
| 33 // The minimum depth a stack should support. | 33 // The minimum depth a stack should support. |
| 34 const int kMinStackDepth = 2; | 34 const int kMinStackDepth = 2; |
| 35 | 35 |
| 36 union ThreadRef { | 36 union ThreadRef { |
| 37 int64_t as_id; | 37 int64_t as_id; |
| 38 #if defined(OS_WIN) | 38 #if defined(OS_WIN) |
| 39 // On Windows, the handle itself is often a pseudo-handle with a common | 39 // On Windows, the handle itself is often a pseudo-handle with a common |
| 40 // value meaning "this thread" and so the thread-id is used. The former | 40 // value meaning "this thread" and so the thread-id is used. The former |
| 41 // can be converted to a thread-id with a system call. | 41 // can be converted to a thread-id with a system call. |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 94 | 94 |
| 95 ActivitySnapshot::ActivitySnapshot() {} | 95 ActivitySnapshot::ActivitySnapshot() {} |
| 96 ActivitySnapshot::~ActivitySnapshot() {} | 96 ActivitySnapshot::~ActivitySnapshot() {} |
| 97 | 97 |
| 98 | 98 |
| 99 // This information is kept for every thread that is tracked. It is filled | 99 // This information is kept for every thread that is tracked. It is filled |
| 100 // the very first time the thread is seen. All fields must be of exact sizes | 100 // the very first time the thread is seen. All fields must be of exact sizes |
| 101 // so there is no issue moving between 32 and 64-bit builds. | 101 // so there is no issue moving between 32 and 64-bit builds. |
| 102 struct ThreadActivityTracker::Header { | 102 struct ThreadActivityTracker::Header { |
| 103 // This unique number indicates a valid initialization of the memory. | 103 // This unique number indicates a valid initialization of the memory. |
| 104 uint64_t cookie; | 104 std::atomic<uint32_t> cookie; |
| 105 uint32_t reserved; // pad out to 64 bits |
| 105 | 106 |
| 106 // The process-id and thread-id (thread_ref.as_id) to which this data belongs. | 107 // The process-id and thread-id (thread_ref.as_id) to which this data belongs. |
| 107 // These identifiers are not guaranteed to mean anything but are unique, in | 108 // These identifiers are not guaranteed to mean anything but are unique, in |
| 108 // combination, among all active trackers. It would be nice to always have | 109 // combination, among all active trackers. It would be nice to always have |
| 109 // the process_id be a 64-bit value but the necessity of having it atomic | 110 // the process_id be a 64-bit value but the necessity of having it atomic |
| 110 // (for the memory barriers it provides) limits it to the natural word size | 111 // (for the memory barriers it provides) limits it to the natural word size |
| 111 // of the machine. | 112 // of the machine. |
| 112 #ifdef ARCH_CPU_64_BITS | 113 #ifdef ARCH_CPU_64_BITS |
| 113 std::atomic<int64_t> process_id; | 114 std::atomic<int64_t> process_id; |
| 114 #else | 115 #else |
| (...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 175 sizeof(header_->thread_ref) == sizeof(header_->thread_ref.as_id), | 176 sizeof(header_->thread_ref) == sizeof(header_->thread_ref.as_id), |
| 176 "PlatformThreadHandle::Handle is too big to hold in 64-bit ID"); | 177 "PlatformThreadHandle::Handle is too big to hold in 64-bit ID"); |
| 177 | 178 |
| 178 // Ensure that the alignment of Activity.data is properly aligned to a | 179 // Ensure that the alignment of Activity.data is properly aligned to a |
| 179 // 64-bit boundary so there are no interoperability-issues across cpu | 180 // 64-bit boundary so there are no interoperability-issues across cpu |
| 180 // architectures. | 181 // architectures. |
| 181 static_assert(offsetof(Activity, data) % sizeof(uint64_t) == 0, | 182 static_assert(offsetof(Activity, data) % sizeof(uint64_t) == 0, |
| 182 "ActivityData.data is not 64-bit aligned"); | 183 "ActivityData.data is not 64-bit aligned"); |
| 183 | 184 |
| 184 // Provided memory should either be completely initialized or all zeros. | 185 // Provided memory should either be completely initialized or all zeros. |
| 185 if (header_->cookie == 0) { | 186 if (header_->cookie.load(std::memory_order_relaxed) == 0) { |
| 186 // This is a new file. Double-check other fields and then initialize. | 187 // This is a new file. Double-check other fields and then initialize. |
| 187 DCHECK_EQ(0, header_->process_id.load(std::memory_order_relaxed)); | 188 DCHECK_EQ(0, header_->process_id.load(std::memory_order_relaxed)); |
| 188 DCHECK_EQ(0, header_->thread_ref.as_id); | 189 DCHECK_EQ(0, header_->thread_ref.as_id); |
| 189 DCHECK_EQ(0, header_->start_time); | 190 DCHECK_EQ(0, header_->start_time); |
| 190 DCHECK_EQ(0, header_->start_ticks); | 191 DCHECK_EQ(0, header_->start_ticks); |
| 191 DCHECK_EQ(0U, header_->stack_slots); | 192 DCHECK_EQ(0U, header_->stack_slots); |
| 192 DCHECK_EQ(0U, header_->current_depth.load(std::memory_order_relaxed)); | 193 DCHECK_EQ(0U, header_->current_depth.load(std::memory_order_relaxed)); |
| 193 DCHECK_EQ(0U, header_->stack_unchanged.load(std::memory_order_relaxed)); | 194 DCHECK_EQ(0U, header_->stack_unchanged.load(std::memory_order_relaxed)); |
| 194 DCHECK_EQ(0, stack_[0].time_internal); | 195 DCHECK_EQ(0, stack_[0].time_internal); |
| 195 DCHECK_EQ(0U, stack_[0].origin_address); | 196 DCHECK_EQ(0U, stack_[0].origin_address); |
| 196 DCHECK_EQ(0U, stack_[0].call_stack[0]); | 197 DCHECK_EQ(0U, stack_[0].call_stack[0]); |
| 197 DCHECK_EQ(0U, stack_[0].data.task.sequence_id); | 198 DCHECK_EQ(0U, stack_[0].data.task.sequence_id); |
| 198 | 199 |
| 199 #if defined(OS_WIN) | 200 #if defined(OS_WIN) |
| 200 header_->thread_ref.as_tid = PlatformThread::CurrentId(); | 201 header_->thread_ref.as_tid = PlatformThread::CurrentId(); |
| 201 #elif defined(OS_POSIX) | 202 #elif defined(OS_POSIX) |
| 202 header_->thread_ref.as_handle = | 203 header_->thread_ref.as_handle = |
| 203 PlatformThread::CurrentHandle().platform_handle(); | 204 PlatformThread::CurrentHandle().platform_handle(); |
| 204 #endif | 205 #endif |
| 206 header_->process_id.store(GetCurrentProcId(), std::memory_order_relaxed); |
| 207 |
| 205 header_->start_time = base::Time::Now().ToInternalValue(); | 208 header_->start_time = base::Time::Now().ToInternalValue(); |
| 206 header_->start_ticks = base::TimeTicks::Now().ToInternalValue(); | 209 header_->start_ticks = base::TimeTicks::Now().ToInternalValue(); |
| 207 header_->stack_slots = stack_slots_; | 210 header_->stack_slots = stack_slots_; |
| 208 strlcpy(header_->thread_name, PlatformThread::GetName(), | 211 strlcpy(header_->thread_name, PlatformThread::GetName(), |
| 209 sizeof(header_->thread_name)); | 212 sizeof(header_->thread_name)); |
| 210 header_->cookie = kHeaderCookie; | |
| 211 | 213 |
| 212 // This is done last so as to guarantee that everything above is "released" | 214 // This is done last so as to guarantee that everything above is "released" |
| 213 // by the time this value gets written. | 215 // by the time this value gets written. |
| 214 header_->process_id.store(GetCurrentProcId(), std::memory_order_release); | 216 header_->cookie.store(kHeaderCookie, std::memory_order_release); |
| 215 | 217 |
| 216 valid_ = true; | 218 valid_ = true; |
| 217 DCHECK(IsValid()); | 219 DCHECK(IsValid()); |
| 218 } else { | 220 } else { |
| 219 // This is a file with existing data. Perform basic consistency checks. | 221 // This is a file with existing data. Perform basic consistency checks. |
| 220 valid_ = true; | 222 valid_ = true; |
| 221 valid_ = IsValid(); | 223 valid_ = IsValid(); |
| 222 } | 224 } |
| 223 } | 225 } |
| 224 | 226 |
| (...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 298 // The stack has shrunk meaning that some other thread trying to copy the | 300 // The stack has shrunk meaning that some other thread trying to copy the |
| 299 // contents for reporting purposes could get bad data. That thread would | 301 // contents for reporting purposes could get bad data. That thread would |
| 300 // have written a non-zero value into |stack_unchanged|; clearing it here | 302 // have written a non-zero value into |stack_unchanged|; clearing it here |
| 301 // will let that thread detect that something did change. This needs to | 303 // will let that thread detect that something did change. This needs to |
| 302 // happen after the atomic |depth| operation above so a "release" store | 304 // happen after the atomic |depth| operation above so a "release" store |
| 303 // is required. | 305 // is required. |
| 304 header_->stack_unchanged.store(0, std::memory_order_release); | 306 header_->stack_unchanged.store(0, std::memory_order_release); |
| 305 } | 307 } |
| 306 | 308 |
| 307 bool ThreadActivityTracker::IsValid() const { | 309 bool ThreadActivityTracker::IsValid() const { |
| 308 if (header_->cookie != kHeaderCookie || | 310 if (header_->cookie.load(std::memory_order_acquire) != kHeaderCookie || |
| 309 header_->process_id.load(std::memory_order_relaxed) == 0 || | 311 header_->process_id.load(std::memory_order_relaxed) == 0 || |
| 310 header_->thread_ref.as_id == 0 || | 312 header_->thread_ref.as_id == 0 || |
| 311 header_->start_time == 0 || | 313 header_->start_time == 0 || |
| 312 header_->start_ticks == 0 || | 314 header_->start_ticks == 0 || |
| 313 header_->stack_slots != stack_slots_ || | 315 header_->stack_slots != stack_slots_ || |
| 314 header_->thread_name[sizeof(header_->thread_name) - 1] != '\0') { | 316 header_->thread_name[sizeof(header_->thread_name) - 1] != '\0') { |
| 315 return false; | 317 return false; |
| 316 } | 318 } |
| 317 | 319 |
| 318 return valid_; | 320 return valid_; |
| (...skipping 449 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 768 const base::Process* process) | 770 const base::Process* process) |
| 769 : GlobalActivityTracker::ScopedThreadActivity( | 771 : GlobalActivityTracker::ScopedThreadActivity( |
| 770 nullptr, | 772 nullptr, |
| 771 Activity::ACT_PROCESS_WAIT, | 773 Activity::ACT_PROCESS_WAIT, |
| 772 ActivityData::ForProcess(process->Pid()), | 774 ActivityData::ForProcess(process->Pid()), |
| 773 /*lock_allowed=*/true) {} | 775 /*lock_allowed=*/true) {} |
| 774 #endif | 776 #endif |
| 775 | 777 |
| 776 } // namespace debug | 778 } // namespace debug |
| 777 } // namespace base | 779 } // namespace base |
| OLD | NEW |