OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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/trace_event_impl.h" | 5 #include "base/debug/trace_event_impl.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 | 8 |
9 #include "base/bind.h" | 9 #include "base/bind.h" |
10 #include "base/debug/leak_annotations.h" | |
10 #include "base/debug/trace_event.h" | 11 #include "base/debug/trace_event.h" |
11 #include "base/file_util.h" | 12 #include "base/file_util.h" |
12 #include "base/format_macros.h" | 13 #include "base/format_macros.h" |
13 #include "base/lazy_instance.h" | 14 #include "base/lazy_instance.h" |
14 #include "base/memory/singleton.h" | 15 #include "base/memory/singleton.h" |
15 #include "base/process_util.h" | 16 #include "base/process_util.h" |
17 #include "base/stl_util.h" | |
16 #include "base/stringprintf.h" | 18 #include "base/stringprintf.h" |
17 #include "base/string_tokenizer.h" | 19 #include "base/string_tokenizer.h" |
20 #include "base/string_util.h" | |
21 #include "base/sys_info.h" | |
22 #include "base/third_party/dynamic_annotations/dynamic_annotations.h" | |
18 #include "base/threading/platform_thread.h" | 23 #include "base/threading/platform_thread.h" |
19 #include "base/threading/thread_local.h" | 24 #include "base/threading/thread_local.h" |
25 #include "base/time.h" | |
20 #include "base/utf_string_conversions.h" | 26 #include "base/utf_string_conversions.h" |
21 #include "base/stl_util.h" | |
22 #include "base/sys_info.h" | |
23 #include "base/third_party/dynamic_annotations/dynamic_annotations.h" | |
24 #include "base/time.h" | |
25 | 27 |
26 #if defined(OS_WIN) | 28 #if defined(OS_WIN) |
27 #include "base/debug/trace_event_win.h" | 29 #include "base/debug/trace_event_win.h" |
28 #endif | 30 #endif |
29 | 31 |
30 class DeleteTraceLogForTesting { | 32 class DeleteTraceLogForTesting { |
31 public: | 33 public: |
32 static void Delete() { | 34 static void Delete() { |
33 Singleton<base::debug::TraceLog, | 35 Singleton<base::debug::TraceLog, |
34 StaticMemorySingletonTraits<base::debug::TraceLog> >::OnExit(0); | 36 StaticMemorySingletonTraits<base::debug::TraceLog> >::OnExit(0); |
(...skipping 274 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
309 void TraceResultBuffer::Finish() { | 311 void TraceResultBuffer::Finish() { |
310 output_callback_.Run("]"); | 312 output_callback_.Run("]"); |
311 } | 313 } |
312 | 314 |
313 //////////////////////////////////////////////////////////////////////////////// | 315 //////////////////////////////////////////////////////////////////////////////// |
314 // | 316 // |
315 // TraceLog | 317 // TraceLog |
316 // | 318 // |
317 //////////////////////////////////////////////////////////////////////////////// | 319 //////////////////////////////////////////////////////////////////////////////// |
318 | 320 |
321 TraceLog::NotificationHelper::NotificationHelper(TraceLog* trace_log) : | |
322 trace_log_(trace_log), | |
jar (doing other things)
2012/08/29 23:35:45
not quite: When you can't fit all initializers on
jbates
2012/08/30 00:55:14
Done.
| |
323 notification_(0) { | |
324 } | |
325 | |
326 TraceLog::NotificationHelper::~NotificationHelper() { | |
327 } | |
328 | |
329 void TraceLog::NotificationHelper::AddNotificationWhileLocked( | |
330 int notification) { | |
331 if (trace_log_->notification_callback_.is_null()) | |
332 return; | |
333 if (notification_ == 0) { | |
334 callback_copy_ = trace_log_->notification_callback_; | |
335 ++trace_log_->notification_thread_count_; | |
336 } | |
337 notification_ |= notification; | |
338 } | |
339 | |
340 void TraceLog::NotificationHelper::SendNotificationIfAny() { | |
341 if (!notification_) | |
342 return; | |
343 | |
344 callback_copy_.Run(notification_); | |
345 callback_copy_.Reset(); | |
346 | |
347 AutoLock lock(trace_log_->lock_); | |
348 --trace_log_->notification_thread_count_; | |
349 // Signal other waiters (if any) in case they are waiting for the | |
350 // callback object to be unreferenced. | |
351 if (trace_log_->notification_thread_count_ == 0) | |
352 trace_log_->notification_condition_.Signal(); | |
353 } | |
354 | |
319 // static | 355 // static |
320 TraceLog* TraceLog::GetInstance() { | 356 TraceLog* TraceLog::GetInstance() { |
321 return Singleton<TraceLog, StaticMemorySingletonTraits<TraceLog> >::get(); | 357 return Singleton<TraceLog, StaticMemorySingletonTraits<TraceLog> >::get(); |
322 } | 358 } |
323 | 359 |
324 TraceLog::TraceLog() | 360 TraceLog::TraceLog() : |
325 : enabled_(false) | 361 enabled_(false), |
jar (doing other things)
2012/08/29 23:35:45
Nit: fix format for initializer (as described abov
jbates
2012/08/30 00:55:14
Done.
| |
326 , dispatching_to_observer_list_(false) { | 362 notification_condition_(&lock_), |
363 notification_thread_count_(0), | |
364 dispatching_to_observer_list_(false), | |
365 watch_category_(NULL) { | |
327 // Trace is enabled or disabled on one thread while other threads are | 366 // Trace is enabled or disabled on one thread while other threads are |
328 // accessing the enabled flag. We don't care whether edge-case events are | 367 // accessing the enabled flag. We don't care whether edge-case events are |
329 // traced or not, so we allow races on the enabled flag to keep the trace | 368 // traced or not, so we allow races on the enabled flag to keep the trace |
330 // macros fast. | 369 // macros fast. |
331 // TODO(jbates): ANNOTATE_BENIGN_RACE_SIZED crashes windows TSAN bots: | 370 // TODO(jbates): ANNOTATE_BENIGN_RACE_SIZED crashes windows TSAN bots: |
332 // ANNOTATE_BENIGN_RACE_SIZED(g_category_enabled, sizeof(g_category_enabled), | 371 // ANNOTATE_BENIGN_RACE_SIZED(g_category_enabled, sizeof(g_category_enabled), |
333 // "trace_event category enabled"); | 372 // "trace_event category enabled"); |
334 for (int i = 0; i < TRACE_EVENT_MAX_CATEGORIES; ++i) { | 373 for (int i = 0; i < TRACE_EVENT_MAX_CATEGORIES; ++i) { |
335 ANNOTATE_BENIGN_RACE(&g_category_enabled[i], | 374 ANNOTATE_BENIGN_RACE(&g_category_enabled[i], |
336 "trace_event category enabled"); | 375 "trace_event category enabled"); |
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
399 for (int i = 0; i < g_category_index; i++) { | 438 for (int i = 0; i < g_category_index; i++) { |
400 if (strcmp(g_categories[i], name) == 0) | 439 if (strcmp(g_categories[i], name) == 0) |
401 return &g_category_enabled[i]; | 440 return &g_category_enabled[i]; |
402 } | 441 } |
403 | 442 |
404 // Create a new category | 443 // Create a new category |
405 DCHECK(g_category_index < TRACE_EVENT_MAX_CATEGORIES) << | 444 DCHECK(g_category_index < TRACE_EVENT_MAX_CATEGORIES) << |
406 "must increase TRACE_EVENT_MAX_CATEGORIES"; | 445 "must increase TRACE_EVENT_MAX_CATEGORIES"; |
407 if (g_category_index < TRACE_EVENT_MAX_CATEGORIES) { | 446 if (g_category_index < TRACE_EVENT_MAX_CATEGORIES) { |
408 int new_index = g_category_index++; | 447 int new_index = g_category_index++; |
409 g_categories[new_index] = name; | 448 // Don't hold on to the name pointer, so that we can create categories with |
449 // strings not known at compile time (this is required by SetWatchEvent). | |
450 const char* new_name = base::strdup(name); | |
451 ANNOTATE_LEAKING_OBJECT_PTR(new_name); | |
452 g_categories[new_index] = new_name; | |
410 DCHECK(!g_category_enabled[new_index]); | 453 DCHECK(!g_category_enabled[new_index]); |
411 if (enabled_) { | 454 if (enabled_) { |
412 // Note that if both included and excluded_categories are empty, the else | 455 // Note that if both included and excluded_categories are empty, the else |
413 // clause below excludes nothing, thereby enabling this category. | 456 // clause below excludes nothing, thereby enabling this category. |
414 if (!included_categories_.empty()) | 457 if (!included_categories_.empty()) |
415 EnableMatchingCategory(new_index, included_categories_, 1); | 458 EnableMatchingCategory(new_index, included_categories_, 1); |
416 else | 459 else |
417 EnableMatchingCategory(new_index, excluded_categories_, 0); | 460 EnableMatchingCategory(new_index, excluded_categories_, 0); |
418 } else { | 461 } else { |
419 g_category_enabled[new_index] = 0; | 462 g_category_enabled[new_index] = 0; |
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
484 std::vector<std::string>* included_out, | 527 std::vector<std::string>* included_out, |
485 std::vector<std::string>* excluded_out) { | 528 std::vector<std::string>* excluded_out) { |
486 AutoLock lock(lock_); | 529 AutoLock lock(lock_); |
487 if (enabled_) { | 530 if (enabled_) { |
488 *included_out = included_categories_; | 531 *included_out = included_categories_; |
489 *excluded_out = excluded_categories_; | 532 *excluded_out = excluded_categories_; |
490 } | 533 } |
491 } | 534 } |
492 | 535 |
493 void TraceLog::SetDisabled() { | 536 void TraceLog::SetDisabled() { |
494 { | 537 AutoLock lock(lock_); |
495 AutoLock lock(lock_); | 538 if (!enabled_) |
496 if (!enabled_) | 539 return; |
497 return; | |
498 | 540 |
499 if (dispatching_to_observer_list_) { | 541 if (dispatching_to_observer_list_) { |
500 DLOG(ERROR) | 542 DLOG(ERROR) |
501 << "Cannot manipulate TraceLog::Enabled state from an observer."; | 543 << "Cannot manipulate TraceLog::Enabled state from an observer."; |
502 return; | 544 return; |
503 } | 545 } |
504 | 546 |
505 dispatching_to_observer_list_ = true; | 547 dispatching_to_observer_list_ = true; |
506 FOR_EACH_OBSERVER(EnabledStateChangedObserver, enabled_state_observer_list_, | 548 FOR_EACH_OBSERVER(EnabledStateChangedObserver, enabled_state_observer_list_, |
507 OnTraceLogWillDisable()); | 549 OnTraceLogWillDisable()); |
508 dispatching_to_observer_list_ = false; | 550 dispatching_to_observer_list_ = false; |
509 | 551 |
510 enabled_ = false; | 552 enabled_ = false; |
511 included_categories_.clear(); | 553 included_categories_.clear(); |
512 excluded_categories_.clear(); | 554 excluded_categories_.clear(); |
513 for (int i = 0; i < g_category_index; i++) | 555 watch_category_ = NULL; |
514 g_category_enabled[i] = 0; | 556 watch_event_name_ = ""; |
515 AddThreadNameMetadataEvents(); | 557 for (int i = 0; i < g_category_index; i++) |
516 AddClockSyncMetadataEvents(); | 558 g_category_enabled[i] = 0; |
517 } // release lock | 559 AddThreadNameMetadataEvents(); |
518 Flush(); | 560 AddClockSyncMetadataEvents(); |
519 } | 561 } |
520 | 562 |
521 void TraceLog::SetEnabled(bool enabled) { | 563 void TraceLog::SetEnabled(bool enabled) { |
522 if (enabled) | 564 if (enabled) |
523 SetEnabled(std::vector<std::string>(), std::vector<std::string>()); | 565 SetEnabled(std::vector<std::string>(), std::vector<std::string>()); |
524 else | 566 else |
525 SetDisabled(); | 567 SetDisabled(); |
526 } | 568 } |
527 | 569 |
528 void TraceLog::AddEnabledStateObserver(EnabledStateChangedObserver* listener) { | 570 void TraceLog::AddEnabledStateObserver(EnabledStateChangedObserver* listener) { |
529 enabled_state_observer_list_.AddObserver(listener); | 571 enabled_state_observer_list_.AddObserver(listener); |
530 } | 572 } |
531 | 573 |
532 void TraceLog::RemoveEnabledStateObserver( | 574 void TraceLog::RemoveEnabledStateObserver( |
533 EnabledStateChangedObserver* listener) { | 575 EnabledStateChangedObserver* listener) { |
534 enabled_state_observer_list_.RemoveObserver(listener); | 576 enabled_state_observer_list_.RemoveObserver(listener); |
535 } | 577 } |
536 | 578 |
537 float TraceLog::GetBufferPercentFull() const { | 579 float TraceLog::GetBufferPercentFull() const { |
538 return (float)((double)logged_events_.size()/(double)kTraceEventBufferSize); | 580 return (float)((double)logged_events_.size()/(double)kTraceEventBufferSize); |
539 } | 581 } |
540 | 582 |
541 void TraceLog::SetOutputCallback(const TraceLog::OutputCallback& cb) { | 583 void TraceLog::SetNotificationCallback( |
584 const TraceLog::NotificationCallback& cb) { | |
542 AutoLock lock(lock_); | 585 AutoLock lock(lock_); |
543 output_callback_ = cb; | 586 // Wait until other threads are done using the callback. |
587 while (notification_thread_count_ > 0) | |
588 notification_condition_.Wait(); | |
589 notification_callback_ = cb; | |
544 } | 590 } |
545 | 591 |
546 void TraceLog::SetBufferFullCallback(const TraceLog::BufferFullCallback& cb) { | 592 void TraceLog::Flush(const TraceLog::OutputCallback& cb) { |
547 AutoLock lock(lock_); | |
548 buffer_full_callback_ = cb; | |
549 } | |
550 | |
551 void TraceLog::Flush() { | |
552 std::vector<TraceEvent> previous_logged_events; | 593 std::vector<TraceEvent> previous_logged_events; |
553 OutputCallback output_callback_copy; | |
554 { | 594 { |
555 AutoLock lock(lock_); | 595 AutoLock lock(lock_); |
556 previous_logged_events.swap(logged_events_); | 596 previous_logged_events.swap(logged_events_); |
557 output_callback_copy = output_callback_; | |
558 } // release lock | 597 } // release lock |
559 | 598 |
560 if (output_callback_copy.is_null()) | |
561 return; | |
562 | |
563 for (size_t i = 0; | 599 for (size_t i = 0; |
564 i < previous_logged_events.size(); | 600 i < previous_logged_events.size(); |
565 i += kTraceEventBatchSize) { | 601 i += kTraceEventBatchSize) { |
566 scoped_refptr<RefCountedString> json_events_str_ptr = | 602 scoped_refptr<RefCountedString> json_events_str_ptr = |
567 new RefCountedString(); | 603 new RefCountedString(); |
568 TraceEvent::AppendEventsAsJSON(previous_logged_events, | 604 TraceEvent::AppendEventsAsJSON(previous_logged_events, |
569 i, | 605 i, |
570 kTraceEventBatchSize, | 606 kTraceEventBatchSize, |
571 &(json_events_str_ptr->data())); | 607 &(json_events_str_ptr->data())); |
572 output_callback_copy.Run(json_events_str_ptr); | 608 cb.Run(json_events_str_ptr); |
573 } | 609 } |
574 } | 610 } |
575 | 611 |
576 int TraceLog::AddTraceEvent(char phase, | 612 int TraceLog::AddTraceEvent(char phase, |
577 const unsigned char* category_enabled, | 613 const unsigned char* category_enabled, |
578 const char* name, | 614 const char* name, |
579 unsigned long long id, | 615 unsigned long long id, |
580 int num_args, | 616 int num_args, |
581 const char** arg_names, | 617 const char** arg_names, |
582 const unsigned char* arg_types, | 618 const unsigned char* arg_types, |
583 const unsigned long long* arg_values, | 619 const unsigned long long* arg_values, |
584 int threshold_begin_id, | 620 int threshold_begin_id, |
585 long long threshold, | 621 long long threshold, |
586 unsigned char flags) { | 622 unsigned char flags) { |
587 DCHECK(name); | 623 DCHECK(name); |
588 TimeTicks now = TimeTicks::NowFromSystemTraceTime(); | 624 TimeTicks now = TimeTicks::NowFromSystemTraceTime(); |
589 BufferFullCallback buffer_full_callback_copy; | 625 NotificationHelper notifier(this); |
590 int ret_begin_id = -1; | 626 int ret_begin_id = -1; |
591 { | 627 { |
592 AutoLock lock(lock_); | 628 AutoLock lock(lock_); |
593 if (!*category_enabled) | 629 if (!*category_enabled) |
594 return -1; | 630 return -1; |
595 if (logged_events_.size() >= kTraceEventBufferSize) | 631 if (logged_events_.size() >= kTraceEventBufferSize) |
596 return -1; | 632 return -1; |
597 | 633 |
598 int thread_id = static_cast<int>(PlatformThread::CurrentId()); | 634 int thread_id = static_cast<int>(PlatformThread::CurrentId()); |
599 | 635 |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
645 if (flags & TRACE_EVENT_FLAG_MANGLE_ID) | 681 if (flags & TRACE_EVENT_FLAG_MANGLE_ID) |
646 id ^= process_id_hash_; | 682 id ^= process_id_hash_; |
647 | 683 |
648 ret_begin_id = static_cast<int>(logged_events_.size()); | 684 ret_begin_id = static_cast<int>(logged_events_.size()); |
649 logged_events_.push_back( | 685 logged_events_.push_back( |
650 TraceEvent(thread_id, | 686 TraceEvent(thread_id, |
651 now, phase, category_enabled, name, id, | 687 now, phase, category_enabled, name, id, |
652 num_args, arg_names, arg_types, arg_values, | 688 num_args, arg_names, arg_types, arg_values, |
653 flags)); | 689 flags)); |
654 | 690 |
655 if (logged_events_.size() == kTraceEventBufferSize) { | 691 if (logged_events_.size() == kTraceEventBufferSize) |
656 buffer_full_callback_copy = buffer_full_callback_; | 692 notifier.AddNotificationWhileLocked(TRACE_BUFFER_FULL); |
657 } | 693 |
694 if (watch_category_ == category_enabled && watch_event_name_ == name) | |
695 notifier.AddNotificationWhileLocked(EVENT_WATCH_NOTIFICATION); | |
658 } // release lock | 696 } // release lock |
659 | 697 |
660 if (!buffer_full_callback_copy.is_null()) | 698 notifier.SendNotificationIfAny(); |
661 buffer_full_callback_copy.Run(); | |
662 | 699 |
663 return ret_begin_id; | 700 return ret_begin_id; |
664 } | 701 } |
665 | 702 |
666 void TraceLog::AddTraceEventEtw(char phase, | 703 void TraceLog::AddTraceEventEtw(char phase, |
667 const char* name, | 704 const char* name, |
668 const void* id, | 705 const void* id, |
669 const char* extra) { | 706 const char* extra) { |
670 #if defined(OS_WIN) | 707 #if defined(OS_WIN) |
671 TraceEventETWProvider::Trace(name, phase, id, extra); | 708 TraceEventETWProvider::Trace(name, phase, id, extra); |
672 #endif | 709 #endif |
673 INTERNAL_TRACE_EVENT_ADD(phase, "ETW Trace Event", name, | 710 INTERNAL_TRACE_EVENT_ADD(phase, "ETW Trace Event", name, |
674 TRACE_EVENT_FLAG_COPY, "id", id, "extra", extra); | 711 TRACE_EVENT_FLAG_COPY, "id", id, "extra", extra); |
675 } | 712 } |
676 | 713 |
677 void TraceLog::AddTraceEventEtw(char phase, | 714 void TraceLog::AddTraceEventEtw(char phase, |
678 const char* name, | 715 const char* name, |
679 const void* id, | 716 const void* id, |
680 const std::string& extra) | 717 const std::string& extra) |
681 { | 718 { |
682 #if defined(OS_WIN) | 719 #if defined(OS_WIN) |
683 TraceEventETWProvider::Trace(name, phase, id, extra); | 720 TraceEventETWProvider::Trace(name, phase, id, extra); |
684 #endif | 721 #endif |
685 INTERNAL_TRACE_EVENT_ADD(phase, "ETW Trace Event", name, | 722 INTERNAL_TRACE_EVENT_ADD(phase, "ETW Trace Event", name, |
686 TRACE_EVENT_FLAG_COPY, "id", id, "extra", extra); | 723 TRACE_EVENT_FLAG_COPY, "id", id, "extra", extra); |
687 } | 724 } |
688 | 725 |
726 void TraceLog::SetWatchEvent(const std::string& category_name, | |
727 const std::string& event_name) { | |
728 const unsigned char* category = GetCategoryEnabled(category_name.c_str()); | |
729 int notify_count = 0; | |
730 { | |
731 AutoLock lock(lock_); | |
732 watch_category_ = category; | |
733 watch_event_name_ = event_name; | |
734 | |
735 // First, search existing events for watch event because we want to catch it | |
736 // even if it has already occurred. | |
737 for (size_t i = 0u; i < logged_events_.size(); ++i) { | |
738 if (category == logged_events_[i].category_enabled() && | |
739 strcmp(event_name.c_str(), logged_events_[i].name()) == 0) { | |
740 ++notify_count; | |
741 } | |
742 } | |
743 } // release lock | |
744 | |
745 // Send notification for each event found. | |
746 for (int i = 0; i < notify_count; ++i) { | |
747 NotificationHelper notifier(this); | |
748 lock_.Acquire(); | |
749 notifier.AddNotificationWhileLocked(EVENT_WATCH_NOTIFICATION); | |
750 lock_.Release(); | |
751 notifier.SendNotificationIfAny(); | |
752 } | |
753 } | |
754 | |
755 void TraceLog::CancelWatchEvent() { | |
756 AutoLock lock(lock_); | |
757 watch_category_ = NULL; | |
758 watch_event_name_ = ""; | |
759 } | |
760 | |
689 void TraceLog::AddClockSyncMetadataEvents() { | 761 void TraceLog::AddClockSyncMetadataEvents() { |
690 #if defined(OS_ANDROID) | 762 #if defined(OS_ANDROID) |
691 // Since Android does not support sched_setaffinity, we cannot establish clock | 763 // Since Android does not support sched_setaffinity, we cannot establish clock |
692 // sync unless the scheduler clock is set to global. If the trace_clock file | 764 // sync unless the scheduler clock is set to global. If the trace_clock file |
693 // can't be read, we will assume the kernel doesn't support tracing and do | 765 // can't be read, we will assume the kernel doesn't support tracing and do |
694 // nothing. | 766 // nothing. |
695 std::string clock_mode; | 767 std::string clock_mode; |
696 if (!file_util::ReadFileToString( | 768 if (!file_util::ReadFileToString( |
697 FilePath("/sys/kernel/debug/tracing/trace_clock"), | 769 FilePath("/sys/kernel/debug/tracing/trace_clock"), |
698 &clock_mode)) | 770 &clock_mode)) |
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
760 // Create a FNV hash from the process ID for XORing. | 832 // Create a FNV hash from the process ID for XORing. |
761 // See http://isthe.com/chongo/tech/comp/fnv/ for algorithm details. | 833 // See http://isthe.com/chongo/tech/comp/fnv/ for algorithm details. |
762 unsigned long long offset_basis = 14695981039346656037ull; | 834 unsigned long long offset_basis = 14695981039346656037ull; |
763 unsigned long long fnv_prime = 1099511628211ull; | 835 unsigned long long fnv_prime = 1099511628211ull; |
764 unsigned long long pid = static_cast<unsigned long long>(process_id_); | 836 unsigned long long pid = static_cast<unsigned long long>(process_id_); |
765 process_id_hash_ = (offset_basis ^ pid) * fnv_prime; | 837 process_id_hash_ = (offset_basis ^ pid) * fnv_prime; |
766 } | 838 } |
767 | 839 |
768 } // namespace debug | 840 } // namespace debug |
769 } // namespace base | 841 } // namespace base |
OLD | NEW |