diff options
Diffstat (limited to 'base/tracked_objects_unittest.cc')
-rw-r--r-- | base/tracked_objects_unittest.cc | 412 |
1 files changed, 305 insertions, 107 deletions
diff --git a/base/tracked_objects_unittest.cc b/base/tracked_objects_unittest.cc index ef88183..f09e444 100644 --- a/base/tracked_objects_unittest.cc +++ b/base/tracked_objects_unittest.cc @@ -15,15 +15,22 @@ namespace tracked_objects { class TrackedObjectsTest : public testing::Test { public: - ~TrackedObjectsTest() { - ThreadData::ShutdownSingleThreadedCleanup(); - } - + TrackedObjectsTest() { + // On entry, leak any database structures in case they are still in use by + // prior threads. + ThreadData::ShutdownSingleThreadedCleanup(true); + } + + ~TrackedObjectsTest() { + // We should not need to leak any structures we create, since we are + // single threaded, and carefully accounting for items. + ThreadData::ShutdownSingleThreadedCleanup(false); + } }; TEST_F(TrackedObjectsTest, MinimalStartupShutdown) { // Minimal test doesn't even create any tasks. - if (!ThreadData::StartTracking(true)) + if (!ThreadData::InitializeAndSetTrackingStatus(true)) return; EXPECT_FALSE(ThreadData::first()); // No activity even on this thread. @@ -38,10 +45,11 @@ TEST_F(TrackedObjectsTest, MinimalStartupShutdown) { ThreadData::DeathMap death_map; data->SnapshotDeathMap(&death_map); EXPECT_EQ(0u, death_map.size()); - ThreadData::ShutdownSingleThreadedCleanup(); + // Cleanup with no leaking. + ThreadData::ShutdownSingleThreadedCleanup(false); // Do it again, just to be sure we reset state completely. - ThreadData::StartTracking(true); + ThreadData::InitializeAndSetTrackingStatus(true); EXPECT_FALSE(ThreadData::first()); // No activity even on this thread. data = ThreadData::Get(); EXPECT_TRUE(ThreadData::first()); // Now class was constructed. @@ -57,7 +65,7 @@ TEST_F(TrackedObjectsTest, MinimalStartupShutdown) { } TEST_F(TrackedObjectsTest, TinyStartupShutdown) { - if (!ThreadData::StartTracking(true)) + if (!ThreadData::InitializeAndSetTrackingStatus(true)) return; // Instigate tracking on a single tracked object, on our thread. @@ -77,14 +85,14 @@ TEST_F(TrackedObjectsTest, TinyStartupShutdown) { EXPECT_EQ(0u, death_map.size()); // No deaths. - // Now instigate a birth, and a death. - const Births* second_birth = ThreadData::TallyABirthIfActive(location); - ThreadData::TallyADeathIfActive( - second_birth, - base::TimeTicks(), /* Bogus post_time. */ - base::TimeTicks(), /* Bogus delayed_start_time. */ - base::TimeTicks(), /* Bogus start_run_time. */ - base::TimeTicks() /* Bogus end_run_time */ ); + // Now instigate another birth, and a first death at the same location. + // TrackingInfo will call TallyABirth() during construction. + base::TimeTicks kBogusStartTime; + base::TrackingInfo pending_task(location, kBogusStartTime); + TrackedTime kBogusStartRunTime; + TrackedTime kBogusEndRunTime; + ThreadData::TallyRunOnNamedThreadIfTracking(pending_task, kBogusStartRunTime, + kBogusEndRunTime); birth_map.clear(); data->SnapshotBirthMap(&birth_map); @@ -100,13 +108,13 @@ TEST_F(TrackedObjectsTest, TinyStartupShutdown) { } TEST_F(TrackedObjectsTest, DeathDataTest) { - if (!ThreadData::StartTracking(true)) + if (!ThreadData::InitializeAndSetTrackingStatus(true)) return; scoped_ptr<DeathData> data(new DeathData()); ASSERT_NE(data, reinterpret_cast<DeathData*>(NULL)); - EXPECT_EQ(data->run_duration(), base::TimeDelta()); - EXPECT_EQ(data->queue_duration(), base::TimeDelta()); + EXPECT_EQ(data->run_duration(), Duration()); + EXPECT_EQ(data->queue_duration(), Duration()); EXPECT_EQ(data->AverageMsRunDuration(), 0); EXPECT_EQ(data->AverageMsQueueDuration(), 0); EXPECT_EQ(data->count(), 0); @@ -114,8 +122,8 @@ TEST_F(TrackedObjectsTest, DeathDataTest) { int run_ms = 42; int queue_ms = 8; - base::TimeDelta run_duration = base::TimeDelta().FromMilliseconds(run_ms); - base::TimeDelta queue_duration = base::TimeDelta().FromMilliseconds(queue_ms); + Duration run_duration = Duration().FromMilliseconds(run_ms); + Duration queue_duration = Duration().FromMilliseconds(queue_ms); data->RecordDeath(queue_duration, run_duration); EXPECT_EQ(data->run_duration(), run_duration); EXPECT_EQ(data->queue_duration(), queue_duration); @@ -135,7 +143,7 @@ TEST_F(TrackedObjectsTest, DeathDataTest) { EXPECT_TRUE(dictionary->GetInteger("run_ms", &integer)); EXPECT_EQ(integer, 2 * run_ms); EXPECT_TRUE(dictionary->GetInteger("queue_ms", &integer)); - EXPECT_EQ(integer, 2* queue_ms); + EXPECT_EQ(integer, 2 * queue_ms); EXPECT_TRUE(dictionary->GetInteger("count", &integer)); EXPECT_EQ(integer, 2); @@ -151,20 +159,69 @@ TEST_F(TrackedObjectsTest, DeathDataTest) { EXPECT_EQ(json, birth_only_result); } +TEST_F(TrackedObjectsTest, DeactivatedBirthOnlyToValueWorkerThread) { + // Transition to Deactivated state before doing anything. + if (!ThreadData::InitializeAndSetTrackingStatus(false)) + return; + // We don't initialize system with a thread name, so we're viewed as a worker + // thread. + const int kFakeLineNumber = 173; + const char* kFile = "FixedFileName"; + const char* kFunction = "BirthOnlyToValueWorkerThread"; + Location location(kFunction, kFile, kFakeLineNumber, NULL); + Births* birth = ThreadData::TallyABirthIfActive(location); + // We should now see a NULL birth record. + EXPECT_EQ(birth, reinterpret_cast<Births*>(NULL)); + + scoped_ptr<base::Value> value(ThreadData::ToValue()); + std::string json; + base::JSONWriter::Write(value.get(), false, &json); + std::string birth_only_result = "{" + "\"list\":[" + "]" + "}"; + EXPECT_EQ(json, birth_only_result); +} + +TEST_F(TrackedObjectsTest, DeactivatedBirthOnlyToValueMainThread) { + // Start in the deactivated state. + if (!ThreadData::InitializeAndSetTrackingStatus(false)) + return; + + // Use a well named thread. + ThreadData::InitializeThreadContext("SomeMainThreadName"); + const int kFakeLineNumber = 173; + const char* kFile = "FixedFileName"; + const char* kFunction = "BirthOnlyToValueMainThread"; + Location location(kFunction, kFile, kFakeLineNumber, NULL); + // Do not delete birth. We don't own it. + Births* birth = ThreadData::TallyABirthIfActive(location); + // We expect to not get a birth record. + EXPECT_EQ(birth, reinterpret_cast<Births*>(NULL)); + + scoped_ptr<base::Value> value(ThreadData::ToValue()); + std::string json; + base::JSONWriter::Write(value.get(), false, &json); + std::string birth_only_result = "{" + "\"list\":[" + "]" + "}"; + EXPECT_EQ(json, birth_only_result); +} + TEST_F(TrackedObjectsTest, BirthOnlyToValueWorkerThread) { - if (!ThreadData::StartTracking(true)) + if (!ThreadData::InitializeAndSetTrackingStatus(true)) return; // We don't initialize system with a thread name, so we're viewed as a worker // thread. - int fake_line_number = 173; + const int kFakeLineNumber = 173; const char* kFile = "FixedFileName"; const char* kFunction = "BirthOnlyToValueWorkerThread"; - Location location(kFunction, kFile, fake_line_number, NULL); + Location location(kFunction, kFile, kFakeLineNumber, NULL); Births* birth = ThreadData::TallyABirthIfActive(location); EXPECT_NE(birth, reinterpret_cast<Births*>(NULL)); - int process_type = 3; - scoped_ptr<base::Value> value(ThreadData::ToValue(process_type)); + scoped_ptr<base::Value> value(ThreadData::ToValue()); std::string json; base::JSONWriter::Write(value.get(), false, &json); std::string birth_only_result = "{" @@ -183,28 +240,26 @@ TEST_F(TrackedObjectsTest, BirthOnlyToValueWorkerThread) { "\"line_number\":173" "}" "}" - "]," - "\"process\":3" + "]" "}"; EXPECT_EQ(json, birth_only_result); } TEST_F(TrackedObjectsTest, BirthOnlyToValueMainThread) { - if (!ThreadData::StartTracking(true)) + if (!ThreadData::InitializeAndSetTrackingStatus(true)) return; // Use a well named thread. ThreadData::InitializeThreadContext("SomeMainThreadName"); - int fake_line_number = 173; + const int kFakeLineNumber = 173; const char* kFile = "FixedFileName"; const char* kFunction = "BirthOnlyToValueMainThread"; - Location location(kFunction, kFile, fake_line_number, NULL); + Location location(kFunction, kFile, kFakeLineNumber, NULL); // Do not delete birth. We don't own it. Births* birth = ThreadData::TallyABirthIfActive(location); EXPECT_NE(birth, reinterpret_cast<Births*>(NULL)); - int process_type = 34; - scoped_ptr<base::Value> value(ThreadData::ToValue(process_type)); + scoped_ptr<base::Value> value(ThreadData::ToValue()); std::string json; base::JSONWriter::Write(value.get(), false, &json); std::string birth_only_result = "{" @@ -223,40 +278,97 @@ TEST_F(TrackedObjectsTest, BirthOnlyToValueMainThread) { "\"line_number\":173" "}" "}" - "]," - "\"process\":34" + "]" "}"; EXPECT_EQ(json, birth_only_result); } TEST_F(TrackedObjectsTest, LifeCycleToValueMainThread) { - if (!ThreadData::StartTracking(true)) + if (!ThreadData::InitializeAndSetTrackingStatus(true)) + return; + + // Use a well named thread. + ThreadData::InitializeThreadContext("SomeMainThreadName"); + const int kFakeLineNumber = 236; + const char* kFile = "FixedFileName"; + const char* kFunction = "LifeCycleToValueMainThread"; + Location location(kFunction, kFile, kFakeLineNumber, NULL); + // Do not delete birth. We don't own it. + Births* birth = ThreadData::TallyABirthIfActive(location); + EXPECT_NE(birth, reinterpret_cast<Births*>(NULL)); + + const base::TimeTicks kTimePosted = base::TimeTicks() + + base::TimeDelta::FromMilliseconds(1); + const base::TimeTicks kDelayedStartTime = base::TimeTicks(); + // TrackingInfo will call TallyABirth() during construction. + base::TrackingInfo pending_task(location, kDelayedStartTime); + pending_task.time_posted = kTimePosted; // Overwrite implied Now(). + + const TrackedTime kStartOfRun = TrackedTime() + + Duration::FromMilliseconds(5); + const TrackedTime kEndOfRun = TrackedTime() + Duration::FromMilliseconds(7); + ThreadData::TallyRunOnNamedThreadIfTracking(pending_task, + kStartOfRun, kEndOfRun); + + scoped_ptr<base::Value> value(ThreadData::ToValue()); + std::string json; + base::JSONWriter::Write(value.get(), false, &json); + std::string one_line_result = "{" + "\"list\":[" + "{" + "\"birth_thread\":\"SomeMainThreadName\"," + "\"death_data\":{" + "\"count\":1," + "\"queue_ms\":4," + "\"run_ms\":2" + "}," + "\"death_thread\":\"SomeMainThreadName\"," + "\"location\":{" + "\"file_name\":\"FixedFileName\"," + "\"function_name\":\"LifeCycleToValueMainThread\"," + "\"line_number\":236" + "}" + "}" + "]" + "}"; + EXPECT_EQ(one_line_result, json); +} + +// We will deactivate tracking after the birth, and before the death, and +// demonstrate that the lifecycle is completely tallied. This ensures that +// our tallied births are matched by tallied deaths (except for when the +// task is still running, or is queued). +TEST_F(TrackedObjectsTest, LifeCycleMidDeactivatedToValueMainThread) { + if (!ThreadData::InitializeAndSetTrackingStatus(true)) return; // Use a well named thread. ThreadData::InitializeThreadContext("SomeMainThreadName"); - int fake_line_number = 236; + const int kFakeLineNumber = 236; const char* kFile = "FixedFileName"; const char* kFunction = "LifeCycleToValueMainThread"; - Location location(kFunction, kFile, fake_line_number, NULL); + Location location(kFunction, kFile, kFakeLineNumber, NULL); // Do not delete birth. We don't own it. Births* birth = ThreadData::TallyABirthIfActive(location); EXPECT_NE(birth, reinterpret_cast<Births*>(NULL)); - // TimeTicks initializers ar ein microseconds. Durations are calculated in - // milliseconds, so we need to use 1000x. - const base::TimeTicks time_posted = base::TimeTicks() + - base::TimeDelta::FromMilliseconds(1); - const base::TimeTicks delayed_start_time = base::TimeTicks(); - const base::TimeTicks start_of_run = base::TimeTicks() + - base::TimeDelta::FromMilliseconds(5); - const base::TimeTicks end_of_run = base::TimeTicks() + - base::TimeDelta::FromMilliseconds(7); - ThreadData::TallyADeathIfActive(birth, time_posted, delayed_start_time, - start_of_run, end_of_run); - - int process_type = 7; - scoped_ptr<base::Value> value(ThreadData::ToValue(process_type)); + const base::TimeTicks kTimePosted = base::TimeTicks() + + base::TimeDelta::FromMilliseconds(1); + const base::TimeTicks kDelayedStartTime = base::TimeTicks(); + // TrackingInfo will call TallyABirth() during construction. + base::TrackingInfo pending_task(location, kDelayedStartTime); + pending_task.time_posted = kTimePosted; // Overwrite implied Now(). + + // Turn off tracking now that we have births. + EXPECT_TRUE(ThreadData::InitializeAndSetTrackingStatus(false)); + + const TrackedTime kStartOfRun = TrackedTime() + + Duration::FromMilliseconds(5); + const TrackedTime kEndOfRun = TrackedTime() + Duration::FromMilliseconds(7); + ThreadData::TallyRunOnNamedThreadIfTracking(pending_task, + kStartOfRun, kEndOfRun); + + scoped_ptr<base::Value> value(ThreadData::ToValue()); std::string json; base::JSONWriter::Write(value.get(), false, &json); std::string one_line_result = "{" @@ -275,44 +387,132 @@ TEST_F(TrackedObjectsTest, LifeCycleToValueMainThread) { "\"line_number\":236" "}" "}" - "]," - "\"process\":7" + "]" + "}"; + EXPECT_EQ(one_line_result, json); +} + +// We will deactivate tracking before starting a life cycle, and neither +// the birth nor the death will be recorded. +TEST_F(TrackedObjectsTest, LifeCyclePreDeactivatedToValueMainThread) { + if (!ThreadData::InitializeAndSetTrackingStatus(false)) + return; + + // Use a well named thread. + ThreadData::InitializeThreadContext("SomeMainThreadName"); + const int kFakeLineNumber = 236; + const char* kFile = "FixedFileName"; + const char* kFunction = "LifeCycleToValueMainThread"; + Location location(kFunction, kFile, kFakeLineNumber, NULL); + // Do not delete birth. We don't own it. + Births* birth = ThreadData::TallyABirthIfActive(location); + EXPECT_EQ(birth, reinterpret_cast<Births*>(NULL)); + + const base::TimeTicks kTimePosted = base::TimeTicks() + + base::TimeDelta::FromMilliseconds(1); + const base::TimeTicks kDelayedStartTime = base::TimeTicks(); + // TrackingInfo will call TallyABirth() during construction. + base::TrackingInfo pending_task(location, kDelayedStartTime); + pending_task.time_posted = kTimePosted; // Overwrite implied Now(). + + const TrackedTime kStartOfRun = TrackedTime() + + Duration::FromMilliseconds(5); + const TrackedTime kEndOfRun = TrackedTime() + Duration::FromMilliseconds(7); + ThreadData::TallyRunOnNamedThreadIfTracking(pending_task, + kStartOfRun, kEndOfRun); + + scoped_ptr<base::Value> value(ThreadData::ToValue()); + std::string json; + base::JSONWriter::Write(value.get(), false, &json); + std::string one_line_result = "{" + "\"list\":[" + "]" + "}"; + EXPECT_EQ(one_line_result, json); +} + +TEST_F(TrackedObjectsTest, LifeCycleToValueWorkerThread) { + if (!ThreadData::InitializeAndSetTrackingStatus(true)) + return; + + // Don't initialize thread, so that we appear as a worker thread. + // ThreadData::InitializeThreadContext("SomeMainThreadName"); + + const int kFakeLineNumber = 236; + const char* kFile = "FixedFileName"; + const char* kFunction = "LifeCycleToValueWorkerThread"; + Location location(kFunction, kFile, kFakeLineNumber, NULL); + // Do not delete birth. We don't own it. + Births* birth = ThreadData::TallyABirthIfActive(location); + EXPECT_NE(birth, reinterpret_cast<Births*>(NULL)); + + const TrackedTime kTimePosted = TrackedTime() + Duration::FromMilliseconds(1); + const TrackedTime kStartOfRun = TrackedTime() + + Duration::FromMilliseconds(5); + const TrackedTime kEndOfRun = TrackedTime() + Duration::FromMilliseconds(7); + ThreadData::TallyRunOnWorkerThreadIfTracking(birth, kTimePosted, + kStartOfRun, kEndOfRun); + + scoped_ptr<base::Value> value(ThreadData::ToValue()); + std::string json; + base::JSONWriter::Write(value.get(), false, &json); + std::string one_line_result = "{" + "\"list\":[" + "{" + "\"birth_thread\":\"WorkerThread-1\"," + "\"death_data\":{" + "\"count\":1," + "\"queue_ms\":4," + "\"run_ms\":2" + "}," + "\"death_thread\":\"WorkerThread-1\"," + "\"location\":{" + "\"file_name\":\"FixedFileName\"," + "\"function_name\":\"LifeCycleToValueWorkerThread\"," + "\"line_number\":236" + "}" + "}" + "]" "}"; - EXPECT_EQ(json, one_line_result); + EXPECT_EQ(one_line_result, json); } TEST_F(TrackedObjectsTest, TwoLives) { - if (!ThreadData::StartTracking(true)) + if (!ThreadData::InitializeAndSetTrackingStatus(true)) return; // Use a well named thread. ThreadData::InitializeThreadContext("SomeFileThreadName"); - int fake_line_number = 222; + const int kFakeLineNumber = 222; const char* kFile = "AnotherFileName"; const char* kFunction = "TwoLives"; - Location location(kFunction, kFile, fake_line_number, NULL); + Location location(kFunction, kFile, kFakeLineNumber, NULL); // Do not delete birth. We don't own it. Births* birth = ThreadData::TallyABirthIfActive(location); EXPECT_NE(birth, reinterpret_cast<Births*>(NULL)); - // TimeTicks initializers ar ein microseconds. Durations are calculated in - // milliseconds, so we need to use 1000x. - const base::TimeTicks time_posted = base::TimeTicks() + - base::TimeDelta::FromMilliseconds(1); - const base::TimeTicks delayed_start_time = base::TimeTicks(); - const base::TimeTicks start_of_run = base::TimeTicks() + - base::TimeDelta::FromMilliseconds(5); - const base::TimeTicks end_of_run = base::TimeTicks() + - base::TimeDelta::FromMilliseconds(7); - ThreadData::TallyADeathIfActive(birth, time_posted, delayed_start_time, - start_of_run, end_of_run); - - birth = ThreadData::TallyABirthIfActive(location); - ThreadData::TallyADeathIfActive(birth, time_posted, delayed_start_time, - start_of_run, end_of_run); - - int process_type = 7; - scoped_ptr<base::Value> value(ThreadData::ToValue(process_type)); + + const base::TimeTicks kTimePosted = base::TimeTicks() + + base::TimeDelta::FromMilliseconds(1); + const base::TimeTicks kDelayedStartTime = base::TimeTicks(); + // TrackingInfo will call TallyABirth() during construction. + base::TrackingInfo pending_task(location, kDelayedStartTime); + pending_task.time_posted = kTimePosted; // Overwrite implied Now(). + + const TrackedTime kStartOfRun = TrackedTime() + + Duration::FromMilliseconds(5); + const TrackedTime kEndOfRun = TrackedTime() + Duration::FromMilliseconds(7); + ThreadData::TallyRunOnNamedThreadIfTracking(pending_task, + kStartOfRun, kEndOfRun); + + // TrackingInfo will call TallyABirth() during construction. + base::TrackingInfo pending_task2(location, kDelayedStartTime); + pending_task2.time_posted = kTimePosted; // Overwrite implied Now(). + + ThreadData::TallyRunOnNamedThreadIfTracking(pending_task2, + kStartOfRun, kEndOfRun); + + scoped_ptr<base::Value> value(ThreadData::ToValue()); std::string json; base::JSONWriter::Write(value.get(), false, &json); std::string one_line_result = "{" @@ -331,44 +531,43 @@ TEST_F(TrackedObjectsTest, TwoLives) { "\"line_number\":222" "}" "}" - "]," - "\"process\":7" + "]" "}"; - EXPECT_EQ(json, one_line_result); + EXPECT_EQ(one_line_result, json); } TEST_F(TrackedObjectsTest, DifferentLives) { - if (!ThreadData::StartTracking(true)) + if (!ThreadData::InitializeAndSetTrackingStatus(true)) return; // Use a well named thread. ThreadData::InitializeThreadContext("SomeFileThreadName"); - int fake_line_number = 567; + const int kFakeLineNumber = 567; const char* kFile = "AnotherFileName"; const char* kFunction = "DifferentLives"; - Location location(kFunction, kFile, fake_line_number, NULL); - // Do not delete birth. We don't own it. - Births* birth = ThreadData::TallyABirthIfActive(location); - EXPECT_NE(birth, reinterpret_cast<Births*>(NULL)); + Location location(kFunction, kFile, kFakeLineNumber, NULL); + + const base::TimeTicks kTimePosted = base::TimeTicks() + + base::TimeDelta::FromMilliseconds(1); + const base::TimeTicks kDelayedStartTime = base::TimeTicks(); + // TrackingInfo will call TallyABirth() during construction. + base::TrackingInfo pending_task(location, kDelayedStartTime); + pending_task.time_posted = kTimePosted; // Overwrite implied Now(). + + const TrackedTime kStartOfRun = TrackedTime() + + Duration::FromMilliseconds(5); + const TrackedTime kEndOfRun = TrackedTime() + Duration::FromMilliseconds(7); + ThreadData::TallyRunOnNamedThreadIfTracking(pending_task, + kStartOfRun, kEndOfRun); + + const int kSecondFakeLineNumber = 999; + Location second_location(kFunction, kFile, kSecondFakeLineNumber, NULL); + + // TrackingInfo will call TallyABirth() during construction. + base::TrackingInfo pending_task2(second_location, kDelayedStartTime); + pending_task2.time_posted = kTimePosted; // Overwrite implied Now(). - // TimeTicks initializers ar ein microseconds. Durations are calculated in - // milliseconds, so we need to use 1000x. - const base::TimeTicks time_posted = base::TimeTicks() + - base::TimeDelta::FromMilliseconds(1); - const base::TimeTicks delayed_start_time = base::TimeTicks(); - const base::TimeTicks start_of_run = base::TimeTicks() + - base::TimeDelta::FromMilliseconds(5); - const base::TimeTicks end_of_run = base::TimeTicks() + - base::TimeDelta::FromMilliseconds(7); - ThreadData::TallyADeathIfActive(birth, time_posted, delayed_start_time, - start_of_run, end_of_run); - - int second_fake_line_number = 999; - Location second_location(kFunction, kFile, second_fake_line_number, NULL); - birth = ThreadData::TallyABirthIfActive(second_location); - - int process_type = 2; - scoped_ptr<base::Value> value(ThreadData::ToValue(process_type)); + scoped_ptr<base::Value> value(ThreadData::ToValue()); std::string json; base::JSONWriter::Write(value.get(), false, &json); std::string one_line_result = "{" @@ -401,10 +600,9 @@ TEST_F(TrackedObjectsTest, DifferentLives) { "\"line_number\":999" "}" "}" - "]," - "\"process\":2" + "]" "}"; - EXPECT_EQ(json, one_line_result); + EXPECT_EQ(one_line_result, json); } } // namespace tracked_objects |