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 // Activity tracking provides a low-overhead method of collecting information | 5 // Activity tracking provides a low-overhead method of collecting information |
6 // about the state of the application for analysis both while it is running | 6 // about the state of the application for analysis both while it is running |
7 // and after it has terminated unexpectedly. Its primary purpose is to help | 7 // and after it has terminated unexpectedly. Its primary purpose is to help |
8 // locate reasons the browser becomes unresponsive by providing insight into | 8 // locate reasons the browser becomes unresponsive by providing insight into |
9 // what all the various threads and processes are (or were) doing. | 9 // what all the various threads and processes are (or were) doing. |
10 | 10 |
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
89 uint32_t padding; | 89 uint32_t padding; |
90 int64_t process_id; | 90 int64_t process_id; |
91 int64_t create_stamp; | 91 int64_t create_stamp; |
92 }; | 92 }; |
93 | 93 |
94 // The data associated with an activity is dependent upon the activity type. | 94 // The data associated with an activity is dependent upon the activity type. |
95 // This union defines all of the various fields. All fields must be explicitly | 95 // This union defines all of the various fields. All fields must be explicitly |
96 // sized types to ensure no interoperability problems between 32-bit and | 96 // sized types to ensure no interoperability problems between 32-bit and |
97 // 64-bit systems. | 97 // 64-bit systems. |
98 union ActivityData { | 98 union ActivityData { |
99 // Expected size for 32/64-bit check. | |
100 // TODO(bcwhite): VC2015 doesn't allow statics in unions. Fix when it does. | |
101 // static constexpr size_t kExpectedInstanceSize = 8; | |
102 | |
99 // Generic activities don't have any defined structure. | 103 // Generic activities don't have any defined structure. |
100 struct { | 104 struct { |
101 uint32_t id; // An arbitrary identifier used for association. | 105 uint32_t id; // An arbitrary identifier used for association. |
102 int32_t info; // An arbitrary value used for information purposes. | 106 int32_t info; // An arbitrary value used for information purposes. |
103 } generic; | 107 } generic; |
104 struct { | 108 struct { |
105 uint64_t sequence_id; // The sequence identifier of the posted task. | 109 uint64_t sequence_id; // The sequence identifier of the posted task. |
106 } task; | 110 } task; |
107 struct { | 111 struct { |
108 uint64_t lock_address; // The memory address of the lock object. | 112 uint64_t lock_address; // The memory address of the lock object. |
(...skipping 117 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
226 | 230 |
227 DISALLOW_COPY_AND_ASSIGN(ActivityTrackerMemoryAllocator); | 231 DISALLOW_COPY_AND_ASSIGN(ActivityTrackerMemoryAllocator); |
228 }; | 232 }; |
229 | 233 |
230 | 234 |
231 // This structure is the full contents recorded for every activity pushed | 235 // This structure is the full contents recorded for every activity pushed |
232 // onto the stack. The |activity_type| indicates what is actually stored in | 236 // onto the stack. The |activity_type| indicates what is actually stored in |
233 // the |data| field. All fields must be explicitly sized types to ensure no | 237 // the |data| field. All fields must be explicitly sized types to ensure no |
234 // interoperability problems between 32-bit and 64-bit systems. | 238 // interoperability problems between 32-bit and 64-bit systems. |
235 struct Activity { | 239 struct Activity { |
240 // Expected size for 32/64-bit check. | |
241 static constexpr size_t kExpectedInstanceSize = | |
242 40 + 8 * kActivityCallStackSize + | |
243 /*ActivityData::kExpectedInstanceSize=*/8; | |
244 | |
236 // The type of an activity on the stack. Activities are broken into | 245 // The type of an activity on the stack. Activities are broken into |
237 // categories with the category ID taking the top 4 bits and the lower | 246 // categories with the category ID taking the top 4 bits and the lower |
238 // bits representing an action within that category. This combination | 247 // bits representing an action within that category. This combination |
239 // makes it easy to "switch" based on the type during analysis. | 248 // makes it easy to "switch" based on the type during analysis. |
240 enum Type : uint8_t { | 249 enum Type : uint8_t { |
241 // This "null" constant is used to indicate "do not change" in calls. | 250 // This "null" constant is used to indicate "do not change" in calls. |
242 ACT_NULL = 0, | 251 ACT_NULL = 0, |
243 | 252 |
244 // Task activities involve callbacks posted to a thread or thread-pool | 253 // Task activities involve callbacks posted to a thread or thread-pool |
245 // using the PostTask() method or any of its friends. | 254 // using the PostTask() method or any of its friends. |
(...skipping 13 matching lines...) Expand all Loading... | |
259 // Thread activities involve the life management of threads. | 268 // Thread activities involve the life management of threads. |
260 ACT_THREAD = 4 << 4, | 269 ACT_THREAD = 4 << 4, |
261 ACT_THREAD_START = ACT_THREAD, | 270 ACT_THREAD_START = ACT_THREAD, |
262 ACT_THREAD_JOIN, | 271 ACT_THREAD_JOIN, |
263 | 272 |
264 // Process activities involve the life management of processes. | 273 // Process activities involve the life management of processes. |
265 ACT_PROCESS = 5 << 4, | 274 ACT_PROCESS = 5 << 4, |
266 ACT_PROCESS_START = ACT_PROCESS, | 275 ACT_PROCESS_START = ACT_PROCESS, |
267 ACT_PROCESS_WAIT, | 276 ACT_PROCESS_WAIT, |
268 | 277 |
278 // Exception activities indicate the occurence of something unexpected. | |
279 ACT_EXCEPTION = 14 << 4, | |
280 | |
269 // Generic activities are user defined and can be anything. | 281 // Generic activities are user defined and can be anything. |
270 ACT_GENERIC = 15 << 4, | 282 ACT_GENERIC = 15 << 4, |
271 | 283 |
272 // These constants can be used to separate the category and action from | 284 // These constants can be used to separate the category and action from |
273 // a combined activity type. | 285 // a combined activity type. |
274 ACT_CATEGORY_MASK = 0xF << 4, | 286 ACT_CATEGORY_MASK = 0xF << 4, |
275 ACT_ACTION_MASK = 0xF | 287 ACT_ACTION_MASK = 0xF |
276 }; | 288 }; |
277 | 289 |
278 // Internal representation of time. During collection, this is in "ticks" | 290 // Internal representation of time. During collection, this is in "ticks" |
(...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
370 friend class ActivityUserData; | 382 friend class ActivityUserData; |
371 | 383 |
372 ValueType type_; | 384 ValueType type_; |
373 uint64_t short_value_; // Used to hold copy of numbers, etc. | 385 uint64_t short_value_; // Used to hold copy of numbers, etc. |
374 std::string long_value_; // Used to hold copy of raw/string data. | 386 std::string long_value_; // Used to hold copy of raw/string data. |
375 StringPiece ref_value_; // Used to hold reference to external data. | 387 StringPiece ref_value_; // Used to hold reference to external data. |
376 }; | 388 }; |
377 | 389 |
378 using Snapshot = std::map<std::string, TypedValue>; | 390 using Snapshot = std::map<std::string, TypedValue>; |
379 | 391 |
392 ActivityUserData(); | |
380 ActivityUserData(void* memory, size_t size); | 393 ActivityUserData(void* memory, size_t size); |
381 virtual ~ActivityUserData(); | 394 virtual ~ActivityUserData(); |
382 | 395 |
383 // Gets the unique ID number for this user data. If this changes then the | 396 // Gets the unique ID number for this user data. If this changes then the |
384 // contents have been overwritten by another thread. The return value is | 397 // contents have been overwritten by another thread. The return value is |
385 // always non-zero unless it's actually just a data "sink". | 398 // always non-zero unless it's actually just a data "sink". |
386 uint32_t id() const { | 399 uint32_t id() const { |
387 return header_ ? header_->owner.data_id.load(std::memory_order_relaxed) : 0; | 400 return header_ ? header_->owner.data_id.load(std::memory_order_relaxed) : 0; |
388 } | 401 } |
389 | 402 |
(...skipping 174 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
564 int64_t process_id = 0; | 577 int64_t process_id = 0; |
565 int64_t thread_id = 0; | 578 int64_t thread_id = 0; |
566 | 579 |
567 // The current stack of activities that are underway for this thread. It | 580 // The current stack of activities that are underway for this thread. It |
568 // is limited in its maximum size with later entries being left off. | 581 // is limited in its maximum size with later entries being left off. |
569 std::vector<Activity> activity_stack; | 582 std::vector<Activity> activity_stack; |
570 | 583 |
571 // The current total depth of the activity stack, including those later | 584 // The current total depth of the activity stack, including those later |
572 // entries not recorded in the |activity_stack| vector. | 585 // entries not recorded in the |activity_stack| vector. |
573 uint32_t activity_stack_depth = 0; | 586 uint32_t activity_stack_depth = 0; |
587 | |
588 // The last recorded "exception" activity. | |
589 Activity last_exception; | |
574 }; | 590 }; |
575 | 591 |
576 // This is the base class for having the compiler manage an activity on the | 592 // This is the base class for having the compiler manage an activity on the |
577 // tracker's stack. It does nothing but call methods on the passed |tracker| | 593 // tracker's stack. It does nothing but call methods on the passed |tracker| |
578 // if it is not null, making it safe (and cheap) to create these objects | 594 // if it is not null, making it safe (and cheap) to create these objects |
579 // even if activity tracking is not enabled. | 595 // even if activity tracking is not enabled. |
580 class BASE_EXPORT ScopedActivity { | 596 class BASE_EXPORT ScopedActivity { |
581 public: | 597 public: |
582 ScopedActivity(ThreadActivityTracker* tracker, | 598 ScopedActivity(ThreadActivityTracker* tracker, |
583 const void* program_counter, | 599 const void* program_counter, |
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
647 ActivityTrackerMemoryAllocator* allocator); | 663 ActivityTrackerMemoryAllocator* allocator); |
648 | 664 |
649 // Returns if there is true use-data associated with a given ActivityId since | 665 // Returns if there is true use-data associated with a given ActivityId since |
650 // it's possible than any returned object is just a sink. | 666 // it's possible than any returned object is just a sink. |
651 bool HasUserData(ActivityId id); | 667 bool HasUserData(ActivityId id); |
652 | 668 |
653 // Release the user-data information for an activity. | 669 // Release the user-data information for an activity. |
654 void ReleaseUserData(ActivityId id, | 670 void ReleaseUserData(ActivityId id, |
655 ActivityTrackerMemoryAllocator* allocator); | 671 ActivityTrackerMemoryAllocator* allocator); |
656 | 672 |
673 // Save an exception and return space for additional information. | |
674 ActivityUserData& ExceptionActivity( | |
675 const void* program_counter, | |
676 const void* origin, | |
677 Activity::Type type, | |
678 const ActivityData& data, | |
679 ActivityTrackerMemoryAllocator* allocator); | |
680 | |
657 // Returns whether the current data is valid or not. It is not valid if | 681 // Returns whether the current data is valid or not. It is not valid if |
658 // corruption has been detected in the header or other data structures. | 682 // corruption has been detected in the header or other data structures. |
659 bool IsValid() const; | 683 bool IsValid() const; |
660 | 684 |
661 // Gets a copy of the tracker contents for analysis. Returns false if a | 685 // Gets a copy of the tracker contents for analysis. Returns false if a |
662 // snapshot was not possible, perhaps because the data is not valid; the | 686 // snapshot was not possible, perhaps because the data is not valid; the |
663 // contents of |output_snapshot| are undefined in that case. The current | 687 // contents of |output_snapshot| are undefined in that case. The current |
664 // implementation does not support concurrent snapshot operations. | 688 // implementation does not support concurrent snapshot operations. |
665 bool CreateSnapshot(Snapshot* output_snapshot) const; | 689 bool CreateSnapshot(Snapshot* output_snapshot) const; |
666 | 690 |
(...skipping 10 matching lines...) Expand all Loading... | |
677 ProcessId* out_id, | 701 ProcessId* out_id, |
678 int64_t* out_stamp); | 702 int64_t* out_stamp); |
679 | 703 |
680 // Calculates the memory size required for a given stack depth, including | 704 // Calculates the memory size required for a given stack depth, including |
681 // the internal header structure for the stack. | 705 // the internal header structure for the stack. |
682 static size_t SizeForStackDepth(int stack_depth); | 706 static size_t SizeForStackDepth(int stack_depth); |
683 | 707 |
684 private: | 708 private: |
685 friend class ActivityTrackerTest; | 709 friend class ActivityTrackerTest; |
686 | 710 |
711 std::unique_ptr<ActivityUserData> CreateUserDataForActivity( | |
712 Activity* activity, | |
713 ActivityTrackerMemoryAllocator* allocator); | |
714 | |
687 Header* const header_; // Pointer to the Header structure. | 715 Header* const header_; // Pointer to the Header structure. |
688 Activity* const stack_; // The stack of activities. | 716 Activity* const stack_; // The stack of activities. |
689 const uint32_t stack_slots_; // The total number of stack slots. | 717 const uint32_t stack_slots_; // The total number of stack slots. |
690 | 718 |
691 bool valid_ = false; // Tracks whether the data is valid or not. | 719 bool valid_ = false; // Tracks whether the data is valid or not. |
692 | 720 |
721 // A user-data object for information associated with the last exception. | |
722 std::unique_ptr<ActivityUserData> exception_data; | |
723 | |
693 base::ThreadChecker thread_checker_; | 724 base::ThreadChecker thread_checker_; |
694 | 725 |
695 DISALLOW_COPY_AND_ASSIGN(ThreadActivityTracker); | 726 DISALLOW_COPY_AND_ASSIGN(ThreadActivityTracker); |
696 }; | 727 }; |
697 | 728 |
698 | 729 |
699 // The global tracker manages all the individual thread trackers. Memory for | 730 // The global tracker manages all the individual thread trackers. Memory for |
700 // the thread trackers is taken from a PersistentMemoryAllocator which allows | 731 // the thread trackers is taken from a PersistentMemoryAllocator which allows |
701 // for the data to be analyzed by a parallel process or even post-mortem. | 732 // for the data to be analyzed by a parallel process or even post-mortem. |
702 class BASE_EXPORT GlobalActivityTracker { | 733 class BASE_EXPORT GlobalActivityTracker { |
(...skipping 19 matching lines...) Expand all Loading... | |
722 // An enumeration of common process life stages. All entries are given an | 753 // An enumeration of common process life stages. All entries are given an |
723 // explicit number so they are known and remain constant; this allows for | 754 // explicit number so they are known and remain constant; this allows for |
724 // cross-version analysis either locally or on a server. | 755 // cross-version analysis either locally or on a server. |
725 enum ProcessPhase : int { | 756 enum ProcessPhase : int { |
726 // The phases are generic and may have meaning to the tracker. | 757 // The phases are generic and may have meaning to the tracker. |
727 PROCESS_PHASE_UNKNOWN = 0, | 758 PROCESS_PHASE_UNKNOWN = 0, |
728 PROCESS_LAUNCHED = 1, | 759 PROCESS_LAUNCHED = 1, |
729 PROCESS_LAUNCH_FAILED = 2, | 760 PROCESS_LAUNCH_FAILED = 2, |
730 PROCESS_EXITED_CLEANLY = 10, | 761 PROCESS_EXITED_CLEANLY = 10, |
731 PROCESS_EXITED_WITH_CODE = 11, | 762 PROCESS_EXITED_WITH_CODE = 11, |
763 PROCESS_UNHANDLED_EXCEPTION = 50, | |
manzagop (departed)
2017/02/24 19:06:14
Perhaps this should be kept separate (e.g. a hit-e
bcwhite
2017/02/24 19:10:58
Actually, I need to remove it because it writes to
| |
732 | 764 |
733 // Add here whatever is useful for analysis. | 765 // Add here whatever is useful for analysis. |
734 PROCESS_SHUTDOWN_STARTED = 100, | 766 PROCESS_SHUTDOWN_STARTED = 100, |
735 PROCESS_MAIN_LOOP_STARTED = 101, | 767 PROCESS_MAIN_LOOP_STARTED = 101, |
736 }; | 768 }; |
737 | 769 |
738 // A callback made when a process exits to allow immediate analysis of its | 770 // A callback made when a process exits to allow immediate analysis of its |
739 // data. Note that the system may reuse the |process_id| so when fetching | 771 // data. Note that the system may reuse the |process_id| so when fetching |
740 // records its important to ensure that what is returned was created before | 772 // records its important to ensure that what is returned was created before |
741 // the |exit_stamp|. Movement of |process_data| information is allowed. | 773 // the |exit_stamp|. Movement of |process_data| information is allowed. |
(...skipping 158 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
900 const FilePath::StringType& args) { | 932 const FilePath::StringType& args) { |
901 GlobalActivityTracker* tracker = Get(); | 933 GlobalActivityTracker* tracker = Get(); |
902 if (tracker) | 934 if (tracker) |
903 tracker->RecordProcessLaunch(process_id, exe, args); | 935 tracker->RecordProcessLaunch(process_id, exe, args); |
904 } | 936 } |
905 static void RecordProcessExitIfEnabled(ProcessId process_id, int exit_code) { | 937 static void RecordProcessExitIfEnabled(ProcessId process_id, int exit_code) { |
906 GlobalActivityTracker* tracker = Get(); | 938 GlobalActivityTracker* tracker = Get(); |
907 if (tracker) | 939 if (tracker) |
908 tracker->RecordProcessExit(process_id, exit_code); | 940 tracker->RecordProcessExit(process_id, exit_code); |
909 } | 941 } |
942 | |
910 // Sets the "phase" of the current process, useful for knowing what it was | 943 // Sets the "phase" of the current process, useful for knowing what it was |
911 // doing when it last reported. | 944 // doing when it last reported. |
912 void SetProcessPhase(ProcessPhase phase); | 945 void SetProcessPhase(ProcessPhase phase); |
913 static void SetProcessPhaseIfEnabled(ProcessPhase phase) { | 946 static void SetProcessPhaseIfEnabled(ProcessPhase phase) { |
914 GlobalActivityTracker* tracker = Get(); | 947 GlobalActivityTracker* tracker = Get(); |
915 if (tracker) | 948 if (tracker) |
916 tracker->SetProcessPhase(phase); | 949 tracker->SetProcessPhase(phase); |
917 } | 950 } |
918 | 951 |
919 // Records a log message. The current implementation does NOT recycle these | 952 // Records a log message. The current implementation does NOT recycle these |
920 // only store critical messages such as FATAL ones. | 953 // only store critical messages such as FATAL ones. |
921 void RecordLogMessage(StringPiece message); | 954 void RecordLogMessage(StringPiece message); |
922 | 955 |
923 // Records a module load/unload event. This is safe to call multiple times | 956 // Records a module load/unload event. This is safe to call multiple times |
924 // even with the same information. | 957 // even with the same information. |
925 void RecordModuleInfo(const ModuleInfo& info); | 958 void RecordModuleInfo(const ModuleInfo& info); |
926 | 959 |
927 // Record field trial information. This call is thread-safe. In addition to | 960 // Record field trial information. This call is thread-safe. In addition to |
928 // this, construction of a GlobalActivityTracker will cause all existing | 961 // this, construction of a GlobalActivityTracker will cause all existing |
929 // active field trials to be fetched and recorded. | 962 // active field trials to be fetched and recorded. |
930 void RecordFieldTrial(const std::string& trial_name, StringPiece group_name); | 963 void RecordFieldTrial(const std::string& trial_name, StringPiece group_name); |
931 | 964 |
965 // Record exception information for the current thread and return space where | |
966 // additional information may be recorded. | |
967 // TODO(bcwhite): More parameters (space for 4 + 64 bits). | |
968 ALWAYS_INLINE | |
969 ActivityUserData& RecordException(const void* origin) { | |
970 return RecordExceptionImpl(::tracked_objects::GetProgramCounter(), origin); | |
Sigurður Ásgeirsson
2017/02/24 18:37:41
no - this the program counter will have to come fr
bcwhite
2017/02/24 19:10:58
Every activity holds the PC of where it was create
| |
971 } | |
972 | |
932 // Accesses the process data record for storing arbitrary key/value pairs. | 973 // Accesses the process data record for storing arbitrary key/value pairs. |
933 // Updates to this are thread-safe. | 974 // Updates to this are thread-safe. |
934 ActivityUserData& process_data() { return process_data_; } | 975 ActivityUserData& process_data() { return process_data_; } |
935 | 976 |
936 // Accesses the global data record for storing arbitrary key/value pairs. | 977 // Accesses the global data record for storing arbitrary key/value pairs. |
937 // Updates to this are thread-safe. | 978 // Updates to this are thread-safe. |
938 ActivityUserData& global_data() { return global_data_; } | 979 ActivityUserData& global_data() { return global_data_; } |
939 | 980 |
940 private: | 981 private: |
941 friend class GlobalActivityAnalyzer; | 982 friend class GlobalActivityAnalyzer; |
(...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1039 // Creates a global tracker using a given persistent-memory |allocator| and | 1080 // Creates a global tracker using a given persistent-memory |allocator| and |
1040 // providing the given |stack_depth| to each thread tracker it manages. The | 1081 // providing the given |stack_depth| to each thread tracker it manages. The |
1041 // created object is activated so tracking has already started upon return. | 1082 // created object is activated so tracking has already started upon return. |
1042 GlobalActivityTracker(std::unique_ptr<PersistentMemoryAllocator> allocator, | 1083 GlobalActivityTracker(std::unique_ptr<PersistentMemoryAllocator> allocator, |
1043 int stack_depth); | 1084 int stack_depth); |
1044 | 1085 |
1045 // Returns the memory used by an activity-tracker managed by this class. | 1086 // Returns the memory used by an activity-tracker managed by this class. |
1046 // It is called during the destruction of a ManagedActivityTracker object. | 1087 // It is called during the destruction of a ManagedActivityTracker object. |
1047 void ReturnTrackerMemory(ManagedActivityTracker* tracker); | 1088 void ReturnTrackerMemory(ManagedActivityTracker* tracker); |
1048 | 1089 |
1090 // Records exception information. | |
1091 ActivityUserData& RecordExceptionImpl(const void* pc, const void* origin); | |
1092 | |
1049 // Releases the activity-tracker associcated with thread. It is called | 1093 // Releases the activity-tracker associcated with thread. It is called |
1050 // automatically when a thread is joined and thus there is nothing more to | 1094 // automatically when a thread is joined and thus there is nothing more to |
1051 // be tracked. |value| is a pointer to a ManagedActivityTracker. | 1095 // be tracked. |value| is a pointer to a ManagedActivityTracker. |
1052 static void OnTLSDestroy(void* value); | 1096 static void OnTLSDestroy(void* value); |
1053 | 1097 |
1054 // Does process-exit work. This can be run on any thread. | 1098 // Does process-exit work. This can be run on any thread. |
1055 void CleanupAfterProcess(ProcessId process_id, | 1099 void CleanupAfterProcess(ProcessId process_id, |
1056 int64_t exit_stamp, | 1100 int64_t exit_stamp, |
1057 int exit_code, | 1101 int exit_code, |
1058 std::string&& command_line); | 1102 std::string&& command_line); |
(...skipping 173 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1232 ScopedProcessWaitActivity(const void* program_counter, | 1276 ScopedProcessWaitActivity(const void* program_counter, |
1233 const base::Process* process); | 1277 const base::Process* process); |
1234 DISALLOW_COPY_AND_ASSIGN(ScopedProcessWaitActivity); | 1278 DISALLOW_COPY_AND_ASSIGN(ScopedProcessWaitActivity); |
1235 }; | 1279 }; |
1236 #endif | 1280 #endif |
1237 | 1281 |
1238 } // namespace debug | 1282 } // namespace debug |
1239 } // namespace base | 1283 } // namespace base |
1240 | 1284 |
1241 #endif // BASE_DEBUG_ACTIVITY_TRACKER_H_ | 1285 #endif // BASE_DEBUG_ACTIVITY_TRACKER_H_ |
OLD | NEW |