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 <algorithm> | 7 #include <algorithm> |
8 | 8 |
9 #include "base/debug/stack_trace.h" | 9 #include "base/debug/stack_trace.h" |
10 #include "base/files/file.h" | 10 #include "base/files/file.h" |
11 #include "base/files/file_path.h" | 11 #include "base/files/file_path.h" |
12 #include "base/files/memory_mapped_file.h" | 12 #include "base/files/memory_mapped_file.h" |
13 #include "base/logging.h" | 13 #include "base/logging.h" |
14 #include "base/memory/ptr_util.h" | 14 #include "base/memory/ptr_util.h" |
| 15 #include "base/memory/ptr_util.h" |
15 #include "base/metrics/field_trial.h" | 16 #include "base/metrics/field_trial.h" |
16 #include "base/metrics/histogram_macros.h" | 17 #include "base/metrics/histogram_macros.h" |
17 #include "base/pending_task.h" | 18 #include "base/pending_task.h" |
18 #include "base/process/process.h" | 19 #include "base/process/process.h" |
19 #include "base/process/process_handle.h" | 20 #include "base/process/process_handle.h" |
20 #include "base/stl_util.h" | 21 #include "base/stl_util.h" |
21 #include "base/strings/string_util.h" | 22 #include "base/strings/string_util.h" |
22 #include "base/threading/platform_thread.h" | 23 #include "base/threading/platform_thread.h" |
23 | 24 |
24 namespace base { | 25 namespace base { |
25 namespace debug { | 26 namespace debug { |
26 | 27 |
27 namespace { | 28 namespace { |
28 | 29 |
29 // A number that identifies the memory as having been initialized. It's | 30 // A number that identifies the memory as having been initialized. It's |
30 // arbitrary but happens to be the first 4 bytes of SHA1(ThreadActivityTracker). | 31 // arbitrary but happens to be the first 4 bytes of SHA1(ThreadActivityTracker). |
31 // A version number is added on so that major structure changes won't try to | 32 // A version number is added on so that major structure changes won't try to |
32 // read an older version (since the cookie won't match). | 33 // read an older version (since the cookie won't match). |
33 const uint32_t kHeaderCookie = 0xC0029B24UL + 2; // v2 | 34 const uint32_t kHeaderCookie = 0xC0029B24UL + 2; // v2 |
34 | 35 |
35 // The minimum depth a stack should support. | 36 // The minimum depth a stack should support. |
36 const int kMinStackDepth = 2; | 37 const int kMinStackDepth = 2; |
37 | 38 |
38 // The amount of memory set aside for holding arbitrary user data (key/value | 39 // The amount of memory set aside for holding arbitrary user data (key/value |
39 // pairs) globally or associated with ActivityData entries. | 40 // pairs) globally or associated with ActivityData entries. |
40 const size_t kUserDataSize = 1024; // bytes | 41 const size_t kUserDataSize = 1024; // bytes |
41 const size_t kGlobalDataSize = 1024; // bytes | 42 const size_t kGlobalDataSize = 4096; // bytes |
42 const size_t kMaxUserDataNameLength = | 43 const size_t kMaxUserDataNameLength = |
43 static_cast<size_t>(std::numeric_limits<uint8_t>::max()); | 44 static_cast<size_t>(std::numeric_limits<uint8_t>::max()); |
44 | 45 |
45 union ThreadRef { | 46 union ThreadRef { |
46 int64_t as_id; | 47 int64_t as_id; |
47 #if defined(OS_WIN) | 48 #if defined(OS_WIN) |
48 // On Windows, the handle itself is often a pseudo-handle with a common | 49 // On Windows, the handle itself is often a pseudo-handle with a common |
49 // value meaning "this thread" and so the thread-id is used. The former | 50 // value meaning "this thread" and so the thread-id is used. The former |
50 // can be converted to a thread-id with a system call. | 51 // can be converted to a thread-id with a system call. |
51 PlatformThreadId as_tid; | 52 PlatformThreadId as_tid; |
52 #elif defined(OS_POSIX) | 53 #elif defined(OS_POSIX) |
53 // On Posix, the handle is always a unique identifier so no conversion | 54 // On Posix, the handle is always a unique identifier so no conversion |
54 // needs to be done. However, it's value is officially opaque so there | 55 // needs to be done. However, it's value is officially opaque so there |
55 // is no one correct way to convert it to a numerical identifier. | 56 // is no one correct way to convert it to a numerical identifier. |
56 PlatformThreadHandle::Handle as_handle; | 57 PlatformThreadHandle::Handle as_handle; |
57 #endif | 58 #endif |
58 }; | 59 }; |
59 | 60 |
| 61 // Determines the previous aligned index. |
| 62 size_t RoundDownToAlignment(size_t index, size_t alignment) { |
| 63 return index & (0 - alignment); |
| 64 } |
| 65 |
60 // Determines the next aligned index. | 66 // Determines the next aligned index. |
61 size_t RoundUpToAlignment(size_t index, size_t alignment) { | 67 size_t RoundUpToAlignment(size_t index, size_t alignment) { |
62 return (index + (alignment - 1)) & (0 - alignment); | 68 return (index + (alignment - 1)) & (0 - alignment); |
63 } | 69 } |
64 | 70 |
65 } // namespace | 71 } // namespace |
66 | 72 |
67 | 73 |
68 // It doesn't matter what is contained in this (though it will be all zeros) | 74 // It doesn't matter what is contained in this (though it will be all zeros) |
69 // as only the address of it is important. | 75 // as only the address of it is important. |
(...skipping 117 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
187 size_t i; | 193 size_t i; |
188 for (i = 1; i < stack_depth && i < kActivityCallStackSize; ++i) { | 194 for (i = 1; i < stack_depth && i < kActivityCallStackSize; ++i) { |
189 activity->call_stack[i - 1] = reinterpret_cast<uintptr_t>(stack_addrs[i]); | 195 activity->call_stack[i - 1] = reinterpret_cast<uintptr_t>(stack_addrs[i]); |
190 } | 196 } |
191 activity->call_stack[i - 1] = 0; | 197 activity->call_stack[i - 1] = 0; |
192 #else | 198 #else |
193 activity->call_stack[0] = 0; | 199 activity->call_stack[0] = 0; |
194 #endif | 200 #endif |
195 } | 201 } |
196 | 202 |
197 ActivitySnapshot::ActivitySnapshot() {} | 203 ActivityUserData::TypedValue::TypedValue() {} |
198 ActivitySnapshot::~ActivitySnapshot() {} | 204 ActivityUserData::TypedValue::TypedValue(const TypedValue& other) = default; |
| 205 ActivityUserData::TypedValue::~TypedValue() {} |
| 206 |
| 207 StringPiece ActivityUserData::TypedValue::Get() const { |
| 208 DCHECK_EQ(RAW_VALUE, type); |
| 209 return long_value; |
| 210 } |
| 211 |
| 212 StringPiece ActivityUserData::TypedValue::GetString() const { |
| 213 DCHECK_EQ(STRING_VALUE, type); |
| 214 return long_value; |
| 215 } |
| 216 |
| 217 bool ActivityUserData::TypedValue::GetBool() const { |
| 218 DCHECK_EQ(BOOL_VALUE, type); |
| 219 return short_value != 0; |
| 220 } |
| 221 |
| 222 char ActivityUserData::TypedValue::GetChar() const { |
| 223 DCHECK_EQ(CHAR_VALUE, type); |
| 224 return static_cast<char>(short_value); |
| 225 } |
| 226 |
| 227 int64_t ActivityUserData::TypedValue::GetInt() const { |
| 228 DCHECK_EQ(SIGNED_VALUE, type); |
| 229 return static_cast<int64_t>(short_value); |
| 230 } |
| 231 |
| 232 uint64_t ActivityUserData::TypedValue::GetUint() const { |
| 233 DCHECK_EQ(UNSIGNED_VALUE, type); |
| 234 return static_cast<uint64_t>(short_value); |
| 235 } |
| 236 |
| 237 StringPiece ActivityUserData::TypedValue::GetReference() const { |
| 238 DCHECK_EQ(RAW_VALUE_REFERENCE, type); |
| 239 return ref_value; |
| 240 } |
| 241 |
| 242 StringPiece ActivityUserData::TypedValue::GetStringReference() const { |
| 243 DCHECK_EQ(STRING_VALUE_REFERENCE, type); |
| 244 return ref_value; |
| 245 } |
199 | 246 |
200 ActivityUserData::ValueInfo::ValueInfo() {} | 247 ActivityUserData::ValueInfo::ValueInfo() {} |
201 ActivityUserData::ValueInfo::ValueInfo(ValueInfo&&) = default; | 248 ActivityUserData::ValueInfo::ValueInfo(ValueInfo&&) = default; |
202 ActivityUserData::ValueInfo::~ValueInfo() {} | 249 ActivityUserData::ValueInfo::~ValueInfo() {} |
203 | 250 |
| 251 std::atomic<uint32_t> ActivityUserData::next_id_; |
| 252 |
204 ActivityUserData::ActivityUserData(void* memory, size_t size) | 253 ActivityUserData::ActivityUserData(void* memory, size_t size) |
205 : memory_(static_cast<char*>(memory)), available_(size) {} | 254 : memory_(reinterpret_cast<char*>(memory)), |
| 255 available_(RoundDownToAlignment(size, kMemoryAlignment)), |
| 256 id_(reinterpret_cast<std::atomic<uint32_t>*>(memory)) { |
| 257 // It's possible that no user data is being stored. |
| 258 if (!memory_) |
| 259 return; |
| 260 |
| 261 DCHECK_LT(kMemoryAlignment, available_); |
| 262 if (id_->load(std::memory_order_relaxed) == 0) { |
| 263 // Generate a new ID and store it in the first 32-bit word of memory_. |
| 264 // |id_| must be non-zero for non-sink instances. |
| 265 uint32_t id; |
| 266 while ((id = next_id_.fetch_add(1, std::memory_order_relaxed)) == 0) |
| 267 ; |
| 268 id_->store(id, std::memory_order_relaxed); |
| 269 DCHECK_NE(0U, id_->load(std::memory_order_relaxed)); |
| 270 } |
| 271 memory_ += kMemoryAlignment; |
| 272 available_ -= kMemoryAlignment; |
| 273 |
| 274 // If there is already data present, load that. This allows the same class |
| 275 // to be used for analysis through snapshots. |
| 276 ImportExistingData(); |
| 277 } |
206 | 278 |
207 ActivityUserData::~ActivityUserData() {} | 279 ActivityUserData::~ActivityUserData() {} |
208 | 280 |
209 void ActivityUserData::Set(StringPiece name, | 281 void ActivityUserData::Set(StringPiece name, |
210 ValueType type, | 282 ValueType type, |
211 const void* memory, | 283 const void* memory, |
212 size_t size) { | 284 size_t size) { |
213 DCHECK(thread_checker_.CalledOnValidThread()); | 285 DCHECK(thread_checker_.CalledOnValidThread()); |
214 DCHECK_GE(std::numeric_limits<uint8_t>::max(), name.length()); | 286 DCHECK_GE(std::numeric_limits<uint8_t>::max(), name.length()); |
215 size = std::min(std::numeric_limits<uint16_t>::max() - (kMemoryAlignment - 1), | 287 size = std::min(std::numeric_limits<uint16_t>::max() - (kMemoryAlignment - 1), |
(...skipping 16 matching lines...) Expand all Loading... |
232 // because there are not alignment constraints on strings, it's set tight | 304 // because there are not alignment constraints on strings, it's set tight |
233 // against the header. Its extent (the reserved space, even if it's not | 305 // against the header. Its extent (the reserved space, even if it's not |
234 // all used) is calculated so that, when pressed against the header, the | 306 // all used) is calculated so that, when pressed against the header, the |
235 // following field will be aligned properly. | 307 // following field will be aligned properly. |
236 size_t name_size = name.length(); | 308 size_t name_size = name.length(); |
237 size_t name_extent = | 309 size_t name_extent = |
238 RoundUpToAlignment(sizeof(Header) + name_size, kMemoryAlignment) - | 310 RoundUpToAlignment(sizeof(Header) + name_size, kMemoryAlignment) - |
239 sizeof(Header); | 311 sizeof(Header); |
240 size_t value_extent = RoundUpToAlignment(size, kMemoryAlignment); | 312 size_t value_extent = RoundUpToAlignment(size, kMemoryAlignment); |
241 | 313 |
242 // The "basic size" is the minimum size of the record. It's possible that | 314 // The "base size" is the size of the header and (padded) string key. Stop |
243 // lengthy values will get truncated but there must be at least some bytes | 315 // now if there's not room enough for even this. |
244 // available. | 316 size_t base_size = sizeof(Header) + name_extent; |
245 size_t basic_size = sizeof(Header) + name_extent + kMemoryAlignment; | 317 if (base_size > available_) |
246 if (basic_size > available_) | 318 return; |
247 return; // No space to store even the smallest value. | |
248 | 319 |
249 // The "full size" is the size for storing the entire value, truncated | 320 // The "full size" is the size for storing the entire value. |
250 // to the amount of available memory. | 321 size_t full_size = std::min(base_size + value_extent, available_); |
251 size_t full_size = | 322 |
252 std::min(sizeof(Header) + name_extent + value_extent, available_); | 323 // If the value is actually a single byte, see if it can be stuffed at the |
253 size = std::min(full_size - sizeof(Header) - name_extent, size); | 324 // end of the name extent rather than wasting kMemoryAlignment bytes. |
| 325 if (size == 1 && name_extent > name_size) { |
| 326 full_size = base_size; |
| 327 --name_extent; |
| 328 --base_size; |
| 329 } |
| 330 |
| 331 // Truncate the stored size to the amount of available memory. Stop now if |
| 332 // there's not any room for even part of the value. |
| 333 size = std::min(full_size - base_size, size); |
| 334 if (size == 0) |
| 335 return; |
254 | 336 |
255 // Allocate a chunk of memory. | 337 // Allocate a chunk of memory. |
256 Header* header = reinterpret_cast<Header*>(memory_); | 338 Header* header = reinterpret_cast<Header*>(memory_); |
257 memory_ += full_size; | 339 memory_ += full_size; |
258 available_ -= full_size; | 340 available_ -= full_size; |
259 | 341 |
260 // Datafill the header and name records. Memory must be zeroed. The |type| | 342 // Datafill the header and name records. Memory must be zeroed. The |type| |
261 // is written last, atomically, to release all the other values. | 343 // is written last, atomically, to release all the other values. |
262 DCHECK_EQ(END_OF_VALUES, header->type.load(std::memory_order_relaxed)); | 344 DCHECK_EQ(END_OF_VALUES, header->type.load(std::memory_order_relaxed)); |
263 DCHECK_EQ(0, header->value_size.load(std::memory_order_relaxed)); | 345 DCHECK_EQ(0, header->value_size.load(std::memory_order_relaxed)); |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
296 void ActivityUserData::SetReference(StringPiece name, | 378 void ActivityUserData::SetReference(StringPiece name, |
297 ValueType type, | 379 ValueType type, |
298 const void* memory, | 380 const void* memory, |
299 size_t size) { | 381 size_t size) { |
300 ReferenceRecord rec; | 382 ReferenceRecord rec; |
301 rec.address = reinterpret_cast<uintptr_t>(memory); | 383 rec.address = reinterpret_cast<uintptr_t>(memory); |
302 rec.size = size; | 384 rec.size = size; |
303 Set(name, type, &rec, sizeof(rec)); | 385 Set(name, type, &rec, sizeof(rec)); |
304 } | 386 } |
305 | 387 |
| 388 void ActivityUserData::ImportExistingData() const { |
| 389 while (available_ > sizeof(Header)) { |
| 390 Header* header = reinterpret_cast<Header*>(memory_); |
| 391 ValueType type = |
| 392 static_cast<ValueType>(header->type.load(std::memory_order_acquire)); |
| 393 if (type == END_OF_VALUES) |
| 394 return; |
| 395 if (header->record_size > available_) |
| 396 return; |
| 397 |
| 398 size_t value_offset = RoundUpToAlignment(sizeof(Header) + header->name_size, |
| 399 kMemoryAlignment); |
| 400 if (header->record_size == value_offset && |
| 401 header->value_size.load(std::memory_order_relaxed) == 1) { |
| 402 value_offset -= 1; |
| 403 } |
| 404 if (value_offset + header->value_size > header->record_size) |
| 405 return; |
| 406 |
| 407 ValueInfo info; |
| 408 info.name = StringPiece(memory_ + sizeof(Header), header->name_size); |
| 409 info.type = type; |
| 410 info.memory = memory_ + value_offset; |
| 411 info.size_ptr = &header->value_size; |
| 412 info.extent = header->record_size - value_offset; |
| 413 |
| 414 StringPiece key(info.name); |
| 415 values_.insert(std::make_pair(key, std::move(info))); |
| 416 |
| 417 memory_ += header->record_size; |
| 418 available_ -= header->record_size; |
| 419 } |
| 420 } |
| 421 |
| 422 bool ActivityUserData::CreateSnapshot(Snapshot* output_snapshot) const { |
| 423 DCHECK(output_snapshot); |
| 424 DCHECK(output_snapshot->empty()); |
| 425 |
| 426 // Find any new data that may have been added by an active instance of this |
| 427 // class that is adding records. |
| 428 ImportExistingData(); |
| 429 |
| 430 for (const auto& entry : values_) { |
| 431 TypedValue value; |
| 432 value.type = entry.second.type; |
| 433 DCHECK_GE(entry.second.extent, |
| 434 entry.second.size_ptr->load(std::memory_order_relaxed)); |
| 435 |
| 436 switch (entry.second.type) { |
| 437 case RAW_VALUE: |
| 438 case STRING_VALUE: |
| 439 value.long_value = |
| 440 std::string(reinterpret_cast<char*>(entry.second.memory), |
| 441 entry.second.size_ptr->load(std::memory_order_relaxed)); |
| 442 break; |
| 443 case RAW_VALUE_REFERENCE: |
| 444 case STRING_VALUE_REFERENCE: { |
| 445 ReferenceRecord* ref = |
| 446 reinterpret_cast<ReferenceRecord*>(entry.second.memory); |
| 447 value.ref_value = StringPiece( |
| 448 reinterpret_cast<char*>(static_cast<uintptr_t>(ref->address)), |
| 449 static_cast<size_t>(ref->size)); |
| 450 } break; |
| 451 case BOOL_VALUE: |
| 452 case CHAR_VALUE: |
| 453 value.short_value = *reinterpret_cast<char*>(entry.second.memory); |
| 454 break; |
| 455 case SIGNED_VALUE: |
| 456 case UNSIGNED_VALUE: |
| 457 value.short_value = *reinterpret_cast<uint64_t*>(entry.second.memory); |
| 458 break; |
| 459 case END_OF_VALUES: // Included for completeness purposes. |
| 460 NOTREACHED(); |
| 461 } |
| 462 auto inserted = output_snapshot->insert( |
| 463 std::make_pair(entry.second.name.as_string(), std::move(value))); |
| 464 DCHECK(inserted.second); // True if inserted, false if existed. |
| 465 } |
| 466 |
| 467 return true; |
| 468 } |
| 469 |
| 470 const void* ActivityUserData::GetBaseAddress() { |
| 471 // The |memory_| pointer advances as elements are written but the |id_| |
| 472 // value is always at the start of the block so just return that. |
| 473 return id_; |
| 474 } |
| 475 |
306 // This information is kept for every thread that is tracked. It is filled | 476 // This information is kept for every thread that is tracked. It is filled |
307 // the very first time the thread is seen. All fields must be of exact sizes | 477 // the very first time the thread is seen. All fields must be of exact sizes |
308 // so there is no issue moving between 32 and 64-bit builds. | 478 // so there is no issue moving between 32 and 64-bit builds. |
309 struct ThreadActivityTracker::Header { | 479 struct ThreadActivityTracker::Header { |
310 // Expected size for 32/64-bit check. | 480 // Expected size for 32/64-bit check. |
311 static constexpr size_t kExpectedInstanceSize = 80; | 481 static constexpr size_t kExpectedInstanceSize = 80; |
312 | 482 |
313 // This unique number indicates a valid initialization of the memory. | 483 // This unique number indicates a valid initialization of the memory. |
314 std::atomic<uint32_t> cookie; | 484 std::atomic<uint32_t> cookie; |
315 | 485 |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
352 // is not the current implementation so no parallel snapshots allowed). | 522 // is not the current implementation so no parallel snapshots allowed). |
353 std::atomic<uint32_t> stack_unchanged; | 523 std::atomic<uint32_t> stack_unchanged; |
354 | 524 |
355 // The name of the thread (up to a maximum length). Dynamic-length names | 525 // The name of the thread (up to a maximum length). Dynamic-length names |
356 // are not practical since the memory has to come from the same persistent | 526 // are not practical since the memory has to come from the same persistent |
357 // allocator that holds this structure and to which this object has no | 527 // allocator that holds this structure and to which this object has no |
358 // reference. | 528 // reference. |
359 char thread_name[32]; | 529 char thread_name[32]; |
360 }; | 530 }; |
361 | 531 |
| 532 ThreadActivityTracker::Snapshot::Snapshot() {} |
| 533 ThreadActivityTracker::Snapshot::~Snapshot() {} |
| 534 |
362 ThreadActivityTracker::ScopedActivity::ScopedActivity( | 535 ThreadActivityTracker::ScopedActivity::ScopedActivity( |
363 ThreadActivityTracker* tracker, | 536 ThreadActivityTracker* tracker, |
364 const void* program_counter, | 537 const void* program_counter, |
365 const void* origin, | 538 const void* origin, |
366 Activity::Type type, | 539 Activity::Type type, |
367 const ActivityData& data) | 540 const ActivityData& data) |
368 : tracker_(tracker) { | 541 : tracker_(tracker) { |
369 if (tracker_) | 542 if (tracker_) |
370 activity_id_ = tracker_->PushActivity(program_counter, origin, type, data); | 543 activity_id_ = tracker_->PushActivity(program_counter, origin, type, data); |
371 } | 544 } |
372 | 545 |
373 ThreadActivityTracker::ScopedActivity::~ScopedActivity() { | 546 ThreadActivityTracker::ScopedActivity::~ScopedActivity() { |
374 if (tracker_) | 547 if (tracker_) |
375 tracker_->PopActivity(activity_id_); | 548 tracker_->PopActivity(activity_id_); |
376 } | 549 } |
377 | 550 |
378 void ThreadActivityTracker::ScopedActivity::ChangeTypeAndData( | 551 void ThreadActivityTracker::ScopedActivity::ChangeTypeAndData( |
379 Activity::Type type, | 552 Activity::Type type, |
380 const ActivityData& data) { | 553 const ActivityData& data) { |
381 if (tracker_) | 554 if (tracker_) |
382 tracker_->ChangeActivity(activity_id_, type, data); | 555 tracker_->ChangeActivity(activity_id_, type, data); |
383 } | 556 } |
384 | 557 |
385 ActivityUserData& ThreadActivityTracker::ScopedActivity::user_data() { | |
386 if (!user_data_) { | |
387 if (tracker_) | |
388 user_data_ = tracker_->GetUserData(activity_id_); | |
389 else | |
390 user_data_ = MakeUnique<ActivityUserData>(nullptr, 0); | |
391 } | |
392 return *user_data_; | |
393 } | |
394 | |
395 ThreadActivityTracker::ThreadActivityTracker(void* base, size_t size) | 558 ThreadActivityTracker::ThreadActivityTracker(void* base, size_t size) |
396 : header_(static_cast<Header*>(base)), | 559 : header_(static_cast<Header*>(base)), |
397 stack_(reinterpret_cast<Activity*>(reinterpret_cast<char*>(base) + | 560 stack_(reinterpret_cast<Activity*>(reinterpret_cast<char*>(base) + |
398 sizeof(Header))), | 561 sizeof(Header))), |
399 stack_slots_( | 562 stack_slots_( |
400 static_cast<uint32_t>((size - sizeof(Header)) / sizeof(Activity))) { | 563 static_cast<uint32_t>((size - sizeof(Header)) / sizeof(Activity))) { |
401 DCHECK(thread_checker_.CalledOnValidThread()); | 564 DCHECK(thread_checker_.CalledOnValidThread()); |
402 | 565 |
403 // Verify the parameters but fail gracefully if they're not valid so that | 566 // Verify the parameters but fail gracefully if they're not valid so that |
404 // production code based on external inputs will not crash. IsValid() will | 567 // production code based on external inputs will not crash. IsValid() will |
(...skipping 135 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
540 header_->current_depth.fetch_sub(1, std::memory_order_relaxed) - 1; | 703 header_->current_depth.fetch_sub(1, std::memory_order_relaxed) - 1; |
541 | 704 |
542 // Validate that everything is running correctly. | 705 // Validate that everything is running correctly. |
543 DCHECK_EQ(id, depth); | 706 DCHECK_EQ(id, depth); |
544 | 707 |
545 // A thread-checker creates a lock to check the thread-id which means | 708 // A thread-checker creates a lock to check the thread-id which means |
546 // re-entry into this code if lock acquisitions are being tracked. | 709 // re-entry into this code if lock acquisitions are being tracked. |
547 DCHECK(stack_[depth].activity_type == Activity::ACT_LOCK_ACQUIRE || | 710 DCHECK(stack_[depth].activity_type == Activity::ACT_LOCK_ACQUIRE || |
548 thread_checker_.CalledOnValidThread()); | 711 thread_checker_.CalledOnValidThread()); |
549 | 712 |
550 // Check if there was any user-data memory. It isn't free'd until later | |
551 // because the call to release it can push something on the stack. | |
552 PersistentMemoryAllocator::Reference user_data = stack_[depth].user_data; | |
553 stack_[depth].user_data = 0; | |
554 | |
555 // The stack has shrunk meaning that some other thread trying to copy the | 713 // The stack has shrunk meaning that some other thread trying to copy the |
556 // contents for reporting purposes could get bad data. That thread would | 714 // contents for reporting purposes could get bad data. That thread would |
557 // have written a non-zero value into |stack_unchanged|; clearing it here | 715 // have written a non-zero value into |stack_unchanged|; clearing it here |
558 // will let that thread detect that something did change. This needs to | 716 // will let that thread detect that something did change. This needs to |
559 // happen after the atomic |depth| operation above so a "release" store | 717 // happen after the atomic |depth| operation above so a "release" store |
560 // is required. | 718 // is required. |
561 header_->stack_unchanged.store(0, std::memory_order_release); | 719 header_->stack_unchanged.store(0, std::memory_order_release); |
562 | |
563 // Release resources located above. All stack processing is done so it's | |
564 // safe if some outside code does another push. | |
565 if (user_data) | |
566 GlobalActivityTracker::Get()->ReleaseUserDataMemory(&user_data); | |
567 } | 720 } |
568 | 721 |
569 std::unique_ptr<ActivityUserData> ThreadActivityTracker::GetUserData( | 722 std::unique_ptr<ActivityUserData> ThreadActivityTracker::GetUserData( |
570 ActivityId id) { | 723 ActivityId id, |
| 724 ActivityTrackerMemoryAllocator* allocator) { |
571 // User-data is only stored for activities actually held in the stack. | 725 // User-data is only stored for activities actually held in the stack. |
572 if (id < stack_slots_) { | 726 if (id < stack_slots_) { |
| 727 // Don't allow user data for lock acquisition as recursion may occur. |
| 728 if (stack_[id].activity_type == Activity::ACT_LOCK_ACQUIRE) { |
| 729 NOTREACHED(); |
| 730 return MakeUnique<ActivityUserData>(nullptr, 0); |
| 731 } |
| 732 |
| 733 // Get (or reuse) a block of memory and create a real UserData object |
| 734 // on it. |
| 735 PersistentMemoryAllocator::Reference ref = allocator->GetObjectReference(); |
573 void* memory = | 736 void* memory = |
574 GlobalActivityTracker::Get()->GetUserDataMemory(&stack_[id].user_data); | 737 allocator->GetAsArray<char>(ref, PersistentMemoryAllocator::kSizeAny); |
575 if (memory) | 738 if (memory) { |
576 return MakeUnique<ActivityUserData>(memory, kUserDataSize); | 739 std::unique_ptr<ActivityUserData> user_data = |
| 740 MakeUnique<ActivityUserData>(memory, kUserDataSize); |
| 741 stack_[id].user_data_ref = ref; |
| 742 stack_[id].user_data_id = user_data->id(); |
| 743 return user_data; |
| 744 } |
577 } | 745 } |
578 | 746 |
579 // Return a dummy object that will still accept (but ignore) Set() calls. | 747 // Return a dummy object that will still accept (but ignore) Set() calls. |
580 return MakeUnique<ActivityUserData>(nullptr, 0); | 748 return MakeUnique<ActivityUserData>(nullptr, 0); |
581 } | 749 } |
582 | 750 |
| 751 bool ThreadActivityTracker::HasUserData(ActivityId id) { |
| 752 // User-data is only stored for activities actually held in the stack. |
| 753 return (id < stack_slots_ && stack_[id].user_data_ref); |
| 754 } |
| 755 |
| 756 void ThreadActivityTracker::ReleaseUserData( |
| 757 ActivityId id, |
| 758 ActivityTrackerMemoryAllocator* allocator) { |
| 759 // User-data is only stored for activities actually held in the stack. |
| 760 if (id < stack_slots_ && stack_[id].user_data_ref) { |
| 761 allocator->ReleaseObjectReference(stack_[id].user_data_ref); |
| 762 stack_[id].user_data_ref = 0; |
| 763 } |
| 764 } |
| 765 |
583 bool ThreadActivityTracker::IsValid() const { | 766 bool ThreadActivityTracker::IsValid() const { |
584 if (header_->cookie.load(std::memory_order_acquire) != kHeaderCookie || | 767 if (header_->cookie.load(std::memory_order_acquire) != kHeaderCookie || |
585 header_->process_id.load(std::memory_order_relaxed) == 0 || | 768 header_->process_id.load(std::memory_order_relaxed) == 0 || |
586 header_->thread_ref.as_id == 0 || | 769 header_->thread_ref.as_id == 0 || |
587 header_->start_time == 0 || | 770 header_->start_time == 0 || |
588 header_->start_ticks == 0 || | 771 header_->start_ticks == 0 || |
589 header_->stack_slots != stack_slots_ || | 772 header_->stack_slots != stack_slots_ || |
590 header_->thread_name[sizeof(header_->thread_name) - 1] != '\0') { | 773 header_->thread_name[sizeof(header_->thread_name) - 1] != '\0') { |
591 return false; | 774 return false; |
592 } | 775 } |
593 | 776 |
594 return valid_; | 777 return valid_; |
595 } | 778 } |
596 | 779 |
597 bool ThreadActivityTracker::Snapshot(ActivitySnapshot* output_snapshot) const { | 780 bool ThreadActivityTracker::CreateSnapshot(Snapshot* output_snapshot) const { |
598 DCHECK(output_snapshot); | 781 DCHECK(output_snapshot); |
599 | 782 |
600 // There is no "called on valid thread" check for this method as it can be | 783 // There is no "called on valid thread" check for this method as it can be |
601 // called from other threads or even other processes. It is also the reason | 784 // called from other threads or even other processes. It is also the reason |
602 // why atomic operations must be used in certain places above. | 785 // why atomic operations must be used in certain places above. |
603 | 786 |
604 // It's possible for the data to change while reading it in such a way that it | 787 // It's possible for the data to change while reading it in such a way that it |
605 // invalidates the read. Make several attempts but don't try forever. | 788 // invalidates the read. Make several attempts but don't try forever. |
606 const int kMaxAttempts = 10; | 789 const int kMaxAttempts = 10; |
607 uint32_t depth; | 790 uint32_t depth; |
(...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
703 } | 886 } |
704 | 887 |
705 // static | 888 // static |
706 size_t ThreadActivityTracker::SizeForStackDepth(int stack_depth) { | 889 size_t ThreadActivityTracker::SizeForStackDepth(int stack_depth) { |
707 return static_cast<size_t>(stack_depth) * sizeof(Activity) + sizeof(Header); | 890 return static_cast<size_t>(stack_depth) * sizeof(Activity) + sizeof(Header); |
708 } | 891 } |
709 | 892 |
710 | 893 |
711 GlobalActivityTracker* GlobalActivityTracker::g_tracker_ = nullptr; | 894 GlobalActivityTracker* GlobalActivityTracker::g_tracker_ = nullptr; |
712 | 895 |
| 896 GlobalActivityTracker::ScopedThreadActivity::ScopedThreadActivity( |
| 897 const void* program_counter, |
| 898 const void* origin, |
| 899 Activity::Type type, |
| 900 const ActivityData& data, |
| 901 bool lock_allowed) |
| 902 : ThreadActivityTracker::ScopedActivity(GetOrCreateTracker(lock_allowed), |
| 903 program_counter, |
| 904 origin, |
| 905 type, |
| 906 data) {} |
| 907 |
| 908 GlobalActivityTracker::ScopedThreadActivity::~ScopedThreadActivity() { |
| 909 if (tracker_ && tracker_->HasUserData(activity_id_)) { |
| 910 GlobalActivityTracker* global = GlobalActivityTracker::Get(); |
| 911 AutoLock lock(global->user_data_allocator_lock_); |
| 912 tracker_->ReleaseUserData(activity_id_, &global->user_data_allocator_); |
| 913 } |
| 914 } |
| 915 |
| 916 ActivityUserData& GlobalActivityTracker::ScopedThreadActivity::user_data() { |
| 917 if (!user_data_) { |
| 918 if (tracker_) { |
| 919 GlobalActivityTracker* global = GlobalActivityTracker::Get(); |
| 920 AutoLock lock(global->user_data_allocator_lock_); |
| 921 user_data_ = |
| 922 tracker_->GetUserData(activity_id_, &global->user_data_allocator_); |
| 923 } else { |
| 924 user_data_ = MakeUnique<ActivityUserData>(nullptr, 0); |
| 925 } |
| 926 } |
| 927 return *user_data_; |
| 928 } |
| 929 |
713 GlobalActivityTracker::ManagedActivityTracker::ManagedActivityTracker( | 930 GlobalActivityTracker::ManagedActivityTracker::ManagedActivityTracker( |
714 PersistentMemoryAllocator::Reference mem_reference, | 931 PersistentMemoryAllocator::Reference mem_reference, |
715 void* base, | 932 void* base, |
716 size_t size) | 933 size_t size) |
717 : ThreadActivityTracker(base, size), | 934 : ThreadActivityTracker(base, size), |
718 mem_reference_(mem_reference), | 935 mem_reference_(mem_reference), |
719 mem_base_(base) {} | 936 mem_base_(base) {} |
720 | 937 |
721 GlobalActivityTracker::ManagedActivityTracker::~ManagedActivityTracker() { | 938 GlobalActivityTracker::ManagedActivityTracker::~ManagedActivityTracker() { |
722 // The global |g_tracker_| must point to the owner of this class since all | 939 // The global |g_tracker_| must point to the owner of this class since all |
(...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
828 return tracker; | 1045 return tracker; |
829 } | 1046 } |
830 | 1047 |
831 void GlobalActivityTracker::ReleaseTrackerForCurrentThreadForTesting() { | 1048 void GlobalActivityTracker::ReleaseTrackerForCurrentThreadForTesting() { |
832 ThreadActivityTracker* tracker = | 1049 ThreadActivityTracker* tracker = |
833 reinterpret_cast<ThreadActivityTracker*>(this_thread_tracker_.Get()); | 1050 reinterpret_cast<ThreadActivityTracker*>(this_thread_tracker_.Get()); |
834 if (tracker) | 1051 if (tracker) |
835 delete tracker; | 1052 delete tracker; |
836 } | 1053 } |
837 | 1054 |
838 void* GlobalActivityTracker::GetUserDataMemory( | |
839 PersistentMemoryAllocator::Reference* reference) { | |
840 if (!*reference) { | |
841 base::AutoLock autolock(user_data_allocator_lock_); | |
842 *reference = user_data_allocator_.GetObjectReference(); | |
843 if (!*reference) | |
844 return nullptr; | |
845 } | |
846 | |
847 void* memory = allocator_->GetAsArray<char>( | |
848 *reference, kTypeIdUserDataRecord, PersistentMemoryAllocator::kSizeAny); | |
849 DCHECK(memory); | |
850 return memory; | |
851 } | |
852 | |
853 void GlobalActivityTracker::ReleaseUserDataMemory( | |
854 PersistentMemoryAllocator::Reference* reference) { | |
855 DCHECK(*reference); | |
856 base::AutoLock autolock(user_data_allocator_lock_); | |
857 user_data_allocator_.ReleaseObjectReference(*reference); | |
858 *reference = PersistentMemoryAllocator::kReferenceNull; | |
859 } | |
860 | |
861 void GlobalActivityTracker::RecordLogMessage(StringPiece message) { | 1055 void GlobalActivityTracker::RecordLogMessage(StringPiece message) { |
862 // Allocate at least one extra byte so the string is NUL terminated. All | 1056 // Allocate at least one extra byte so the string is NUL terminated. All |
863 // memory returned by the allocator is guaranteed to be zeroed. | 1057 // memory returned by the allocator is guaranteed to be zeroed. |
864 PersistentMemoryAllocator::Reference ref = | 1058 PersistentMemoryAllocator::Reference ref = |
865 allocator_->Allocate(message.size() + 1, kTypeIdGlobalLogMessage); | 1059 allocator_->Allocate(message.size() + 1, kTypeIdGlobalLogMessage); |
866 char* memory = allocator_->GetAsArray<char>(ref, kTypeIdGlobalLogMessage, | 1060 char* memory = allocator_->GetAsArray<char>(ref, kTypeIdGlobalLogMessage, |
867 message.size() + 1); | 1061 message.size() + 1); |
868 if (memory) { | 1062 if (memory) { |
869 memcpy(memory, message.data(), message.size()); | 1063 memcpy(memory, message.data(), message.size()); |
870 allocator_->MakeIterable(ref); | 1064 allocator_->MakeIterable(ref); |
(...skipping 25 matching lines...) Expand all Loading... |
896 kTypeIdGlobalDataRecord, | 1090 kTypeIdGlobalDataRecord, |
897 PersistentMemoryAllocator::kSizeAny), | 1091 PersistentMemoryAllocator::kSizeAny), |
898 kGlobalDataSize) { | 1092 kGlobalDataSize) { |
899 // Ensure the passed memory is valid and empty (iterator finds nothing). | 1093 // Ensure the passed memory is valid and empty (iterator finds nothing). |
900 uint32_t type; | 1094 uint32_t type; |
901 DCHECK(!PersistentMemoryAllocator::Iterator(allocator_.get()).GetNext(&type)); | 1095 DCHECK(!PersistentMemoryAllocator::Iterator(allocator_.get()).GetNext(&type)); |
902 | 1096 |
903 // Ensure that there is no other global object and then make this one such. | 1097 // Ensure that there is no other global object and then make this one such. |
904 DCHECK(!g_tracker_); | 1098 DCHECK(!g_tracker_); |
905 g_tracker_ = this; | 1099 g_tracker_ = this; |
| 1100 |
| 1101 // The global user-data record must be iterable in order to be found by an |
| 1102 // analyzer. |
| 1103 allocator_->MakeIterable(allocator_->GetAsReference( |
| 1104 user_data_.GetBaseAddress(), kTypeIdGlobalDataRecord)); |
906 } | 1105 } |
907 | 1106 |
908 GlobalActivityTracker::~GlobalActivityTracker() { | 1107 GlobalActivityTracker::~GlobalActivityTracker() { |
909 DCHECK_EQ(g_tracker_, this); | 1108 DCHECK_EQ(g_tracker_, this); |
910 DCHECK_EQ(0, thread_tracker_count_.load(std::memory_order_relaxed)); | 1109 DCHECK_EQ(0, thread_tracker_count_.load(std::memory_order_relaxed)); |
911 g_tracker_ = nullptr; | 1110 g_tracker_ = nullptr; |
912 } | 1111 } |
913 | 1112 |
914 void GlobalActivityTracker::ReturnTrackerMemory( | 1113 void GlobalActivityTracker::ReturnTrackerMemory( |
915 ManagedActivityTracker* tracker) { | 1114 ManagedActivityTracker* tracker) { |
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1010 : GlobalActivityTracker::ScopedThreadActivity( | 1209 : GlobalActivityTracker::ScopedThreadActivity( |
1011 program_counter, | 1210 program_counter, |
1012 nullptr, | 1211 nullptr, |
1013 Activity::ACT_PROCESS_WAIT, | 1212 Activity::ACT_PROCESS_WAIT, |
1014 ActivityData::ForProcess(process->Pid()), | 1213 ActivityData::ForProcess(process->Pid()), |
1015 /*lock_allowed=*/true) {} | 1214 /*lock_allowed=*/true) {} |
1016 #endif | 1215 #endif |
1017 | 1216 |
1018 } // namespace debug | 1217 } // namespace debug |
1019 } // namespace base | 1218 } // namespace base |
OLD | NEW |