| OLD | NEW |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 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 // Test of classes in the tracked_objects.h classes. | 5 // Test of classes in the tracked_objects.h classes. |
| 6 | 6 |
| 7 #include "base/tracked_objects.h" | 7 #include "base/tracked_objects.h" |
| 8 | 8 |
| 9 #include "base/json/json_writer.h" | 9 #include "base/json/json_writer.h" |
| 10 #include "base/memory/scoped_ptr.h" | 10 #include "base/memory/scoped_ptr.h" |
| 11 #include "base/time.h" | 11 #include "base/time.h" |
| 12 #include "testing/gtest/include/gtest/gtest.h" | 12 #include "testing/gtest/include/gtest/gtest.h" |
| 13 | 13 |
| 14 namespace tracked_objects { | 14 namespace tracked_objects { |
| 15 | 15 |
| 16 class TrackedObjectsTest : public testing::Test { | 16 class TrackedObjectsTest : public testing::Test { |
| 17 public: | 17 public: |
| 18 ~TrackedObjectsTest() { | 18 TrackedObjectsTest() { |
| 19 ThreadData::ShutdownSingleThreadedCleanup(); | 19 // On entry, leak any database structures in case they are still in use by |
| 20 } | 20 // prior threads. |
| 21 ThreadData::ShutdownSingleThreadedCleanup(true); |
| 22 } |
| 21 | 23 |
| 24 ~TrackedObjectsTest() { |
| 25 // We should not need to leak any structures we create, since we are |
| 26 // single threaded, and carefully accounting for items. |
| 27 ThreadData::ShutdownSingleThreadedCleanup(false); |
| 28 } |
| 22 }; | 29 }; |
| 23 | 30 |
| 24 TEST_F(TrackedObjectsTest, MinimalStartupShutdown) { | 31 TEST_F(TrackedObjectsTest, MinimalStartupShutdown) { |
| 25 // Minimal test doesn't even create any tasks. | 32 // Minimal test doesn't even create any tasks. |
| 26 if (!ThreadData::StartTracking(true)) | 33 if (!ThreadData::InitializeAndSetTrackingStatus(true)) |
| 27 return; | 34 return; |
| 28 | 35 |
| 29 EXPECT_FALSE(ThreadData::first()); // No activity even on this thread. | 36 EXPECT_FALSE(ThreadData::first()); // No activity even on this thread. |
| 30 ThreadData* data = ThreadData::Get(); | 37 ThreadData* data = ThreadData::Get(); |
| 31 EXPECT_TRUE(ThreadData::first()); // Now class was constructed. | 38 EXPECT_TRUE(ThreadData::first()); // Now class was constructed. |
| 32 EXPECT_TRUE(data); | 39 EXPECT_TRUE(data); |
| 33 EXPECT_TRUE(!data->next()); | 40 EXPECT_TRUE(!data->next()); |
| 34 EXPECT_EQ(data, ThreadData::Get()); | 41 EXPECT_EQ(data, ThreadData::Get()); |
| 35 ThreadData::BirthMap birth_map; | 42 ThreadData::BirthMap birth_map; |
| 36 data->SnapshotBirthMap(&birth_map); | 43 data->SnapshotBirthMap(&birth_map); |
| 37 EXPECT_EQ(0u, birth_map.size()); | 44 EXPECT_EQ(0u, birth_map.size()); |
| 38 ThreadData::DeathMap death_map; | 45 ThreadData::DeathMap death_map; |
| 39 data->SnapshotDeathMap(&death_map); | 46 data->SnapshotDeathMap(&death_map); |
| 40 EXPECT_EQ(0u, death_map.size()); | 47 EXPECT_EQ(0u, death_map.size()); |
| 41 ThreadData::ShutdownSingleThreadedCleanup(); | 48 // Cleanup with no leaking. |
| 49 ThreadData::ShutdownSingleThreadedCleanup(false); |
| 42 | 50 |
| 43 // Do it again, just to be sure we reset state completely. | 51 // Do it again, just to be sure we reset state completely. |
| 44 ThreadData::StartTracking(true); | 52 ThreadData::InitializeAndSetTrackingStatus(true); |
| 45 EXPECT_FALSE(ThreadData::first()); // No activity even on this thread. | 53 EXPECT_FALSE(ThreadData::first()); // No activity even on this thread. |
| 46 data = ThreadData::Get(); | 54 data = ThreadData::Get(); |
| 47 EXPECT_TRUE(ThreadData::first()); // Now class was constructed. | 55 EXPECT_TRUE(ThreadData::first()); // Now class was constructed. |
| 48 EXPECT_TRUE(data); | 56 EXPECT_TRUE(data); |
| 49 EXPECT_TRUE(!data->next()); | 57 EXPECT_TRUE(!data->next()); |
| 50 EXPECT_EQ(data, ThreadData::Get()); | 58 EXPECT_EQ(data, ThreadData::Get()); |
| 51 birth_map.clear(); | 59 birth_map.clear(); |
| 52 data->SnapshotBirthMap(&birth_map); | 60 data->SnapshotBirthMap(&birth_map); |
| 53 EXPECT_EQ(0u, birth_map.size()); | 61 EXPECT_EQ(0u, birth_map.size()); |
| 54 death_map.clear(); | 62 death_map.clear(); |
| 55 data->SnapshotDeathMap(&death_map); | 63 data->SnapshotDeathMap(&death_map); |
| 56 EXPECT_EQ(0u, death_map.size()); | 64 EXPECT_EQ(0u, death_map.size()); |
| 57 } | 65 } |
| 58 | 66 |
| 59 TEST_F(TrackedObjectsTest, TinyStartupShutdown) { | 67 TEST_F(TrackedObjectsTest, TinyStartupShutdown) { |
| 60 if (!ThreadData::StartTracking(true)) | 68 if (!ThreadData::InitializeAndSetTrackingStatus(true)) |
| 61 return; | 69 return; |
| 62 | 70 |
| 63 // Instigate tracking on a single tracked object, on our thread. | 71 // Instigate tracking on a single tracked object, on our thread. |
| 64 const Location& location = FROM_HERE; | 72 const Location& location = FROM_HERE; |
| 65 ThreadData::TallyABirthIfActive(location); | 73 ThreadData::TallyABirthIfActive(location); |
| 66 | 74 |
| 67 const ThreadData* data = ThreadData::first(); | 75 const ThreadData* data = ThreadData::first(); |
| 68 ASSERT_TRUE(data); | 76 ASSERT_TRUE(data); |
| 69 EXPECT_TRUE(!data->next()); | 77 EXPECT_TRUE(!data->next()); |
| 70 EXPECT_EQ(data, ThreadData::Get()); | 78 EXPECT_EQ(data, ThreadData::Get()); |
| 71 ThreadData::BirthMap birth_map; | 79 ThreadData::BirthMap birth_map; |
| 72 data->SnapshotBirthMap(&birth_map); | 80 data->SnapshotBirthMap(&birth_map); |
| 73 EXPECT_EQ(1u, birth_map.size()); // 1 birth location. | 81 EXPECT_EQ(1u, birth_map.size()); // 1 birth location. |
| 74 EXPECT_EQ(1, birth_map.begin()->second->birth_count()); // 1 birth. | 82 EXPECT_EQ(1, birth_map.begin()->second->birth_count()); // 1 birth. |
| 75 ThreadData::DeathMap death_map; | 83 ThreadData::DeathMap death_map; |
| 76 data->SnapshotDeathMap(&death_map); | 84 data->SnapshotDeathMap(&death_map); |
| 77 EXPECT_EQ(0u, death_map.size()); // No deaths. | 85 EXPECT_EQ(0u, death_map.size()); // No deaths. |
| 78 | 86 |
| 79 | 87 |
| 80 // Now instigate a birth, and a death. | 88 // Now instigate another birth, and a first death at the same location. |
| 81 const Births* second_birth = ThreadData::TallyABirthIfActive(location); | 89 // TrackingInfo will call TallyABirth() during construction. |
| 82 ThreadData::TallyADeathIfActive( | 90 base::TimeTicks kBogusStartTime; |
| 83 second_birth, | 91 base::TrackingInfo pending_task(location, kBogusStartTime); |
| 84 base::TimeTicks(), /* Bogus post_time. */ | 92 TrackedTime kBogusStartRunTime; |
| 85 base::TimeTicks(), /* Bogus delayed_start_time. */ | 93 TrackedTime kBogusEndRunTime; |
| 86 base::TimeTicks(), /* Bogus start_run_time. */ | 94 ThreadData::TallyRunOnNamedThreadIfTracking(pending_task, kBogusStartRunTime, |
| 87 base::TimeTicks() /* Bogus end_run_time */ ); | 95 kBogusEndRunTime); |
| 88 | 96 |
| 89 birth_map.clear(); | 97 birth_map.clear(); |
| 90 data->SnapshotBirthMap(&birth_map); | 98 data->SnapshotBirthMap(&birth_map); |
| 91 EXPECT_EQ(1u, birth_map.size()); // 1 birth location. | 99 EXPECT_EQ(1u, birth_map.size()); // 1 birth location. |
| 92 EXPECT_EQ(2, birth_map.begin()->second->birth_count()); // 2 births. | 100 EXPECT_EQ(2, birth_map.begin()->second->birth_count()); // 2 births. |
| 93 death_map.clear(); | 101 death_map.clear(); |
| 94 data->SnapshotDeathMap(&death_map); | 102 data->SnapshotDeathMap(&death_map); |
| 95 EXPECT_EQ(1u, death_map.size()); // 1 location. | 103 EXPECT_EQ(1u, death_map.size()); // 1 location. |
| 96 EXPECT_EQ(1, death_map.begin()->second.count()); // 1 death. | 104 EXPECT_EQ(1, death_map.begin()->second.count()); // 1 death. |
| 97 | 105 |
| 98 // The births were at the same location as the one known death. | 106 // The births were at the same location as the one known death. |
| 99 EXPECT_EQ(birth_map.begin()->second, death_map.begin()->first); | 107 EXPECT_EQ(birth_map.begin()->second, death_map.begin()->first); |
| 100 } | 108 } |
| 101 | 109 |
| 102 TEST_F(TrackedObjectsTest, DeathDataTest) { | 110 TEST_F(TrackedObjectsTest, DeathDataTest) { |
| 103 if (!ThreadData::StartTracking(true)) | 111 if (!ThreadData::InitializeAndSetTrackingStatus(true)) |
| 104 return; | 112 return; |
| 105 | 113 |
| 106 scoped_ptr<DeathData> data(new DeathData()); | 114 scoped_ptr<DeathData> data(new DeathData()); |
| 107 ASSERT_NE(data, reinterpret_cast<DeathData*>(NULL)); | 115 ASSERT_NE(data, reinterpret_cast<DeathData*>(NULL)); |
| 108 EXPECT_EQ(data->run_duration(), base::TimeDelta()); | 116 EXPECT_EQ(data->run_duration(), Duration()); |
| 109 EXPECT_EQ(data->queue_duration(), base::TimeDelta()); | 117 EXPECT_EQ(data->queue_duration(), Duration()); |
| 110 EXPECT_EQ(data->AverageMsRunDuration(), 0); | 118 EXPECT_EQ(data->AverageMsRunDuration(), 0); |
| 111 EXPECT_EQ(data->AverageMsQueueDuration(), 0); | 119 EXPECT_EQ(data->AverageMsQueueDuration(), 0); |
| 112 EXPECT_EQ(data->count(), 0); | 120 EXPECT_EQ(data->count(), 0); |
| 113 | 121 |
| 114 int run_ms = 42; | 122 int run_ms = 42; |
| 115 int queue_ms = 8; | 123 int queue_ms = 8; |
| 116 | 124 |
| 117 base::TimeDelta run_duration = base::TimeDelta().FromMilliseconds(run_ms); | 125 Duration run_duration = Duration().FromMilliseconds(run_ms); |
| 118 base::TimeDelta queue_duration = base::TimeDelta().FromMilliseconds(queue_ms); | 126 Duration queue_duration = Duration().FromMilliseconds(queue_ms); |
| 119 data->RecordDeath(queue_duration, run_duration); | 127 data->RecordDeath(queue_duration, run_duration); |
| 120 EXPECT_EQ(data->run_duration(), run_duration); | 128 EXPECT_EQ(data->run_duration(), run_duration); |
| 121 EXPECT_EQ(data->queue_duration(), queue_duration); | 129 EXPECT_EQ(data->queue_duration(), queue_duration); |
| 122 EXPECT_EQ(data->AverageMsRunDuration(), run_ms); | 130 EXPECT_EQ(data->AverageMsRunDuration(), run_ms); |
| 123 EXPECT_EQ(data->AverageMsQueueDuration(), queue_ms); | 131 EXPECT_EQ(data->AverageMsQueueDuration(), queue_ms); |
| 124 EXPECT_EQ(data->count(), 1); | 132 EXPECT_EQ(data->count(), 1); |
| 125 | 133 |
| 126 data->RecordDeath(queue_duration, run_duration); | 134 data->RecordDeath(queue_duration, run_duration); |
| 127 EXPECT_EQ(data->run_duration(), run_duration + run_duration); | 135 EXPECT_EQ(data->run_duration(), run_duration + run_duration); |
| 128 EXPECT_EQ(data->queue_duration(), queue_duration + queue_duration); | 136 EXPECT_EQ(data->queue_duration(), queue_duration + queue_duration); |
| 129 EXPECT_EQ(data->AverageMsRunDuration(), run_ms); | 137 EXPECT_EQ(data->AverageMsRunDuration(), run_ms); |
| 130 EXPECT_EQ(data->AverageMsQueueDuration(), queue_ms); | 138 EXPECT_EQ(data->AverageMsQueueDuration(), queue_ms); |
| 131 EXPECT_EQ(data->count(), 2); | 139 EXPECT_EQ(data->count(), 2); |
| 132 | 140 |
| 133 scoped_ptr<base::DictionaryValue> dictionary(data->ToValue()); | 141 scoped_ptr<base::DictionaryValue> dictionary(data->ToValue()); |
| 134 int integer; | 142 int integer; |
| 135 EXPECT_TRUE(dictionary->GetInteger("run_ms", &integer)); | 143 EXPECT_TRUE(dictionary->GetInteger("run_ms", &integer)); |
| 136 EXPECT_EQ(integer, 2 * run_ms); | 144 EXPECT_EQ(integer, 2 * run_ms); |
| 137 EXPECT_TRUE(dictionary->GetInteger("queue_ms", &integer)); | 145 EXPECT_TRUE(dictionary->GetInteger("queue_ms", &integer)); |
| 138 EXPECT_EQ(integer, 2* queue_ms); | 146 EXPECT_EQ(integer, 2 * queue_ms); |
| 139 EXPECT_TRUE(dictionary->GetInteger("count", &integer)); | 147 EXPECT_TRUE(dictionary->GetInteger("count", &integer)); |
| 140 EXPECT_EQ(integer, 2); | 148 EXPECT_EQ(integer, 2); |
| 141 | 149 |
| 142 std::string output; | 150 std::string output; |
| 143 data->WriteHTML(&output); | 151 data->WriteHTML(&output); |
| 144 std::string results = "Lives:2, Run:84ms(42ms/life) Queue:16ms(8ms/life) "; | 152 std::string results = "Lives:2, Run:84ms(42ms/life) Queue:16ms(8ms/life) "; |
| 145 EXPECT_EQ(output, results); | 153 EXPECT_EQ(output, results); |
| 146 | 154 |
| 147 scoped_ptr<base::Value> value(data->ToValue()); | 155 scoped_ptr<base::Value> value(data->ToValue()); |
| 148 std::string json; | 156 std::string json; |
| 149 base::JSONWriter::Write(value.get(), false, &json); | 157 base::JSONWriter::Write(value.get(), false, &json); |
| 150 std::string birth_only_result = "{\"count\":2,\"queue_ms\":16,\"run_ms\":84}"; | 158 std::string birth_only_result = "{\"count\":2,\"queue_ms\":16,\"run_ms\":84}"; |
| 151 EXPECT_EQ(json, birth_only_result); | 159 EXPECT_EQ(json, birth_only_result); |
| 152 } | 160 } |
| 153 | 161 |
| 162 TEST_F(TrackedObjectsTest, DeactivatedBirthOnlyToValueWorkerThread) { |
| 163 // Transition to Deactivated state before doing anything. |
| 164 if (!ThreadData::InitializeAndSetTrackingStatus(false)) |
| 165 return; |
| 166 // We don't initialize system with a thread name, so we're viewed as a worker |
| 167 // thread. |
| 168 const int kFakeLineNumber = 173; |
| 169 const char* kFile = "FixedFileName"; |
| 170 const char* kFunction = "BirthOnlyToValueWorkerThread"; |
| 171 Location location(kFunction, kFile, kFakeLineNumber, NULL); |
| 172 Births* birth = ThreadData::TallyABirthIfActive(location); |
| 173 // We should now see a NULL birth record. |
| 174 EXPECT_EQ(birth, reinterpret_cast<Births*>(NULL)); |
| 175 |
| 176 scoped_ptr<base::Value> value(ThreadData::ToValue()); |
| 177 std::string json; |
| 178 base::JSONWriter::Write(value.get(), false, &json); |
| 179 std::string birth_only_result = "{" |
| 180 "\"list\":[" |
| 181 "]" |
| 182 "}"; |
| 183 EXPECT_EQ(json, birth_only_result); |
| 184 } |
| 185 |
| 186 TEST_F(TrackedObjectsTest, DeactivatedBirthOnlyToValueMainThread) { |
| 187 // Start in the deactivated state. |
| 188 if (!ThreadData::InitializeAndSetTrackingStatus(false)) |
| 189 return; |
| 190 |
| 191 // Use a well named thread. |
| 192 ThreadData::InitializeThreadContext("SomeMainThreadName"); |
| 193 const int kFakeLineNumber = 173; |
| 194 const char* kFile = "FixedFileName"; |
| 195 const char* kFunction = "BirthOnlyToValueMainThread"; |
| 196 Location location(kFunction, kFile, kFakeLineNumber, NULL); |
| 197 // Do not delete birth. We don't own it. |
| 198 Births* birth = ThreadData::TallyABirthIfActive(location); |
| 199 // We expect to not get a birth record. |
| 200 EXPECT_EQ(birth, reinterpret_cast<Births*>(NULL)); |
| 201 |
| 202 scoped_ptr<base::Value> value(ThreadData::ToValue()); |
| 203 std::string json; |
| 204 base::JSONWriter::Write(value.get(), false, &json); |
| 205 std::string birth_only_result = "{" |
| 206 "\"list\":[" |
| 207 "]" |
| 208 "}"; |
| 209 EXPECT_EQ(json, birth_only_result); |
| 210 } |
| 211 |
| 154 TEST_F(TrackedObjectsTest, BirthOnlyToValueWorkerThread) { | 212 TEST_F(TrackedObjectsTest, BirthOnlyToValueWorkerThread) { |
| 155 if (!ThreadData::StartTracking(true)) | 213 if (!ThreadData::InitializeAndSetTrackingStatus(true)) |
| 156 return; | 214 return; |
| 157 // We don't initialize system with a thread name, so we're viewed as a worker | 215 // We don't initialize system with a thread name, so we're viewed as a worker |
| 158 // thread. | 216 // thread. |
| 159 int fake_line_number = 173; | 217 const int kFakeLineNumber = 173; |
| 160 const char* kFile = "FixedFileName"; | 218 const char* kFile = "FixedFileName"; |
| 161 const char* kFunction = "BirthOnlyToValueWorkerThread"; | 219 const char* kFunction = "BirthOnlyToValueWorkerThread"; |
| 162 Location location(kFunction, kFile, fake_line_number, NULL); | 220 Location location(kFunction, kFile, kFakeLineNumber, NULL); |
| 163 Births* birth = ThreadData::TallyABirthIfActive(location); | 221 Births* birth = ThreadData::TallyABirthIfActive(location); |
| 164 EXPECT_NE(birth, reinterpret_cast<Births*>(NULL)); | 222 EXPECT_NE(birth, reinterpret_cast<Births*>(NULL)); |
| 165 | 223 |
| 166 int process_type = 3; | 224 scoped_ptr<base::Value> value(ThreadData::ToValue()); |
| 167 scoped_ptr<base::Value> value(ThreadData::ToValue(process_type)); | |
| 168 std::string json; | 225 std::string json; |
| 169 base::JSONWriter::Write(value.get(), false, &json); | 226 base::JSONWriter::Write(value.get(), false, &json); |
| 170 std::string birth_only_result = "{" | 227 std::string birth_only_result = "{" |
| 171 "\"list\":[" | 228 "\"list\":[" |
| 172 "{" | 229 "{" |
| 173 "\"birth_thread\":\"WorkerThread-1\"," | 230 "\"birth_thread\":\"WorkerThread-1\"," |
| 174 "\"death_data\":{" | 231 "\"death_data\":{" |
| 175 "\"count\":1," | 232 "\"count\":1," |
| 176 "\"queue_ms\":0," | 233 "\"queue_ms\":0," |
| 177 "\"run_ms\":0" | 234 "\"run_ms\":0" |
| 178 "}," | 235 "}," |
| 179 "\"death_thread\":\"Still_Alive\"," | 236 "\"death_thread\":\"Still_Alive\"," |
| 180 "\"location\":{" | 237 "\"location\":{" |
| 181 "\"file_name\":\"FixedFileName\"," | 238 "\"file_name\":\"FixedFileName\"," |
| 182 "\"function_name\":\"BirthOnlyToValueWorkerThread\"," | 239 "\"function_name\":\"BirthOnlyToValueWorkerThread\"," |
| 183 "\"line_number\":173" | 240 "\"line_number\":173" |
| 184 "}" | 241 "}" |
| 185 "}" | 242 "}" |
| 186 "]," | 243 "]" |
| 187 "\"process\":3" | |
| 188 "}"; | 244 "}"; |
| 189 EXPECT_EQ(json, birth_only_result); | 245 EXPECT_EQ(json, birth_only_result); |
| 190 } | 246 } |
| 191 | 247 |
| 192 TEST_F(TrackedObjectsTest, BirthOnlyToValueMainThread) { | 248 TEST_F(TrackedObjectsTest, BirthOnlyToValueMainThread) { |
| 193 if (!ThreadData::StartTracking(true)) | 249 if (!ThreadData::InitializeAndSetTrackingStatus(true)) |
| 194 return; | 250 return; |
| 195 | 251 |
| 196 // Use a well named thread. | 252 // Use a well named thread. |
| 197 ThreadData::InitializeThreadContext("SomeMainThreadName"); | 253 ThreadData::InitializeThreadContext("SomeMainThreadName"); |
| 198 int fake_line_number = 173; | 254 const int kFakeLineNumber = 173; |
| 199 const char* kFile = "FixedFileName"; | 255 const char* kFile = "FixedFileName"; |
| 200 const char* kFunction = "BirthOnlyToValueMainThread"; | 256 const char* kFunction = "BirthOnlyToValueMainThread"; |
| 201 Location location(kFunction, kFile, fake_line_number, NULL); | 257 Location location(kFunction, kFile, kFakeLineNumber, NULL); |
| 202 // Do not delete birth. We don't own it. | 258 // Do not delete birth. We don't own it. |
| 203 Births* birth = ThreadData::TallyABirthIfActive(location); | 259 Births* birth = ThreadData::TallyABirthIfActive(location); |
| 204 EXPECT_NE(birth, reinterpret_cast<Births*>(NULL)); | 260 EXPECT_NE(birth, reinterpret_cast<Births*>(NULL)); |
| 205 | 261 |
| 206 int process_type = 34; | 262 scoped_ptr<base::Value> value(ThreadData::ToValue()); |
| 207 scoped_ptr<base::Value> value(ThreadData::ToValue(process_type)); | |
| 208 std::string json; | 263 std::string json; |
| 209 base::JSONWriter::Write(value.get(), false, &json); | 264 base::JSONWriter::Write(value.get(), false, &json); |
| 210 std::string birth_only_result = "{" | 265 std::string birth_only_result = "{" |
| 211 "\"list\":[" | 266 "\"list\":[" |
| 212 "{" | 267 "{" |
| 213 "\"birth_thread\":\"SomeMainThreadName\"," | 268 "\"birth_thread\":\"SomeMainThreadName\"," |
| 214 "\"death_data\":{" | 269 "\"death_data\":{" |
| 215 "\"count\":1," | 270 "\"count\":1," |
| 216 "\"queue_ms\":0," | 271 "\"queue_ms\":0," |
| 217 "\"run_ms\":0" | 272 "\"run_ms\":0" |
| 218 "}," | 273 "}," |
| 219 "\"death_thread\":\"Still_Alive\"," | 274 "\"death_thread\":\"Still_Alive\"," |
| 220 "\"location\":{" | 275 "\"location\":{" |
| 221 "\"file_name\":\"FixedFileName\"," | 276 "\"file_name\":\"FixedFileName\"," |
| 222 "\"function_name\":\"BirthOnlyToValueMainThread\"," | 277 "\"function_name\":\"BirthOnlyToValueMainThread\"," |
| 223 "\"line_number\":173" | 278 "\"line_number\":173" |
| 224 "}" | 279 "}" |
| 225 "}" | 280 "}" |
| 226 "]," | 281 "]" |
| 227 "\"process\":34" | |
| 228 "}"; | 282 "}"; |
| 229 EXPECT_EQ(json, birth_only_result); | 283 EXPECT_EQ(json, birth_only_result); |
| 230 } | 284 } |
| 231 | 285 |
| 232 TEST_F(TrackedObjectsTest, LifeCycleToValueMainThread) { | 286 TEST_F(TrackedObjectsTest, LifeCycleToValueMainThread) { |
| 233 if (!ThreadData::StartTracking(true)) | 287 if (!ThreadData::InitializeAndSetTrackingStatus(true)) |
| 234 return; | 288 return; |
| 235 | 289 |
| 236 // Use a well named thread. | 290 // Use a well named thread. |
| 237 ThreadData::InitializeThreadContext("SomeMainThreadName"); | 291 ThreadData::InitializeThreadContext("SomeMainThreadName"); |
| 238 int fake_line_number = 236; | 292 const int kFakeLineNumber = 236; |
| 239 const char* kFile = "FixedFileName"; | 293 const char* kFile = "FixedFileName"; |
| 240 const char* kFunction = "LifeCycleToValueMainThread"; | 294 const char* kFunction = "LifeCycleToValueMainThread"; |
| 241 Location location(kFunction, kFile, fake_line_number, NULL); | 295 Location location(kFunction, kFile, kFakeLineNumber, NULL); |
| 242 // Do not delete birth. We don't own it. | 296 // Do not delete birth. We don't own it. |
| 243 Births* birth = ThreadData::TallyABirthIfActive(location); | 297 Births* birth = ThreadData::TallyABirthIfActive(location); |
| 244 EXPECT_NE(birth, reinterpret_cast<Births*>(NULL)); | 298 EXPECT_NE(birth, reinterpret_cast<Births*>(NULL)); |
| 245 | 299 |
| 246 // TimeTicks initializers ar ein microseconds. Durations are calculated in | 300 const base::TimeTicks kTimePosted = base::TimeTicks() |
| 247 // milliseconds, so we need to use 1000x. | 301 + base::TimeDelta::FromMilliseconds(1); |
| 248 const base::TimeTicks time_posted = base::TimeTicks() + | 302 const base::TimeTicks kDelayedStartTime = base::TimeTicks(); |
| 249 base::TimeDelta::FromMilliseconds(1); | 303 // TrackingInfo will call TallyABirth() during construction. |
| 250 const base::TimeTicks delayed_start_time = base::TimeTicks(); | 304 base::TrackingInfo pending_task(location, kDelayedStartTime); |
| 251 const base::TimeTicks start_of_run = base::TimeTicks() + | 305 pending_task.time_posted = kTimePosted; // Overwrite implied Now(). |
| 252 base::TimeDelta::FromMilliseconds(5); | |
| 253 const base::TimeTicks end_of_run = base::TimeTicks() + | |
| 254 base::TimeDelta::FromMilliseconds(7); | |
| 255 ThreadData::TallyADeathIfActive(birth, time_posted, delayed_start_time, | |
| 256 start_of_run, end_of_run); | |
| 257 | 306 |
| 258 int process_type = 7; | 307 const TrackedTime kStartOfRun = TrackedTime() + |
| 259 scoped_ptr<base::Value> value(ThreadData::ToValue(process_type)); | 308 Duration::FromMilliseconds(5); |
| 309 const TrackedTime kEndOfRun = TrackedTime() + Duration::FromMilliseconds(7); |
| 310 ThreadData::TallyRunOnNamedThreadIfTracking(pending_task, |
| 311 kStartOfRun, kEndOfRun); |
| 312 |
| 313 scoped_ptr<base::Value> value(ThreadData::ToValue()); |
| 260 std::string json; | 314 std::string json; |
| 261 base::JSONWriter::Write(value.get(), false, &json); | 315 base::JSONWriter::Write(value.get(), false, &json); |
| 262 std::string one_line_result = "{" | 316 std::string one_line_result = "{" |
| 263 "\"list\":[" | 317 "\"list\":[" |
| 264 "{" | 318 "{" |
| 265 "\"birth_thread\":\"SomeMainThreadName\"," | 319 "\"birth_thread\":\"SomeMainThreadName\"," |
| 266 "\"death_data\":{" | 320 "\"death_data\":{" |
| 267 "\"count\":1," | 321 "\"count\":1," |
| 268 "\"queue_ms\":4," | 322 "\"queue_ms\":4," |
| 269 "\"run_ms\":2" | 323 "\"run_ms\":2" |
| 270 "}," | 324 "}," |
| 271 "\"death_thread\":\"SomeMainThreadName\"," | 325 "\"death_thread\":\"SomeMainThreadName\"," |
| 272 "\"location\":{" | 326 "\"location\":{" |
| 273 "\"file_name\":\"FixedFileName\"," | 327 "\"file_name\":\"FixedFileName\"," |
| 274 "\"function_name\":\"LifeCycleToValueMainThread\"," | 328 "\"function_name\":\"LifeCycleToValueMainThread\"," |
| 275 "\"line_number\":236" | 329 "\"line_number\":236" |
| 276 "}" | 330 "}" |
| 277 "}" | 331 "}" |
| 278 "]," | 332 "]" |
| 279 "\"process\":7" | |
| 280 "}"; | 333 "}"; |
| 281 EXPECT_EQ(json, one_line_result); | 334 EXPECT_EQ(one_line_result, json); |
| 335 } |
| 336 |
| 337 // We will deactivate tracking after the birth, and before the death, and |
| 338 // demonstrate that the lifecycle is completely tallied. This ensures that |
| 339 // our tallied births are matched by tallied deaths (except for when the |
| 340 // task is still running, or is queued). |
| 341 TEST_F(TrackedObjectsTest, LifeCycleMidDeactivatedToValueMainThread) { |
| 342 if (!ThreadData::InitializeAndSetTrackingStatus(true)) |
| 343 return; |
| 344 |
| 345 // Use a well named thread. |
| 346 ThreadData::InitializeThreadContext("SomeMainThreadName"); |
| 347 const int kFakeLineNumber = 236; |
| 348 const char* kFile = "FixedFileName"; |
| 349 const char* kFunction = "LifeCycleToValueMainThread"; |
| 350 Location location(kFunction, kFile, kFakeLineNumber, NULL); |
| 351 // Do not delete birth. We don't own it. |
| 352 Births* birth = ThreadData::TallyABirthIfActive(location); |
| 353 EXPECT_NE(birth, reinterpret_cast<Births*>(NULL)); |
| 354 |
| 355 const base::TimeTicks kTimePosted = base::TimeTicks() |
| 356 + base::TimeDelta::FromMilliseconds(1); |
| 357 const base::TimeTicks kDelayedStartTime = base::TimeTicks(); |
| 358 // TrackingInfo will call TallyABirth() during construction. |
| 359 base::TrackingInfo pending_task(location, kDelayedStartTime); |
| 360 pending_task.time_posted = kTimePosted; // Overwrite implied Now(). |
| 361 |
| 362 // Turn off tracking now that we have births. |
| 363 EXPECT_TRUE(ThreadData::InitializeAndSetTrackingStatus(false)); |
| 364 |
| 365 const TrackedTime kStartOfRun = TrackedTime() + |
| 366 Duration::FromMilliseconds(5); |
| 367 const TrackedTime kEndOfRun = TrackedTime() + Duration::FromMilliseconds(7); |
| 368 ThreadData::TallyRunOnNamedThreadIfTracking(pending_task, |
| 369 kStartOfRun, kEndOfRun); |
| 370 |
| 371 scoped_ptr<base::Value> value(ThreadData::ToValue()); |
| 372 std::string json; |
| 373 base::JSONWriter::Write(value.get(), false, &json); |
| 374 std::string one_line_result = "{" |
| 375 "\"list\":[" |
| 376 "{" |
| 377 "\"birth_thread\":\"SomeMainThreadName\"," |
| 378 "\"death_data\":{" |
| 379 "\"count\":1," |
| 380 "\"queue_ms\":4," |
| 381 "\"run_ms\":2" |
| 382 "}," |
| 383 "\"death_thread\":\"SomeMainThreadName\"," |
| 384 "\"location\":{" |
| 385 "\"file_name\":\"FixedFileName\"," |
| 386 "\"function_name\":\"LifeCycleToValueMainThread\"," |
| 387 "\"line_number\":236" |
| 388 "}" |
| 389 "}" |
| 390 "]" |
| 391 "}"; |
| 392 EXPECT_EQ(one_line_result, json); |
| 393 } |
| 394 |
| 395 // We will deactivate tracking before starting a life cycle, and neither |
| 396 // the birth nor the death will be recorded. |
| 397 TEST_F(TrackedObjectsTest, LifeCyclePreDeactivatedToValueMainThread) { |
| 398 if (!ThreadData::InitializeAndSetTrackingStatus(false)) |
| 399 return; |
| 400 |
| 401 // Use a well named thread. |
| 402 ThreadData::InitializeThreadContext("SomeMainThreadName"); |
| 403 const int kFakeLineNumber = 236; |
| 404 const char* kFile = "FixedFileName"; |
| 405 const char* kFunction = "LifeCycleToValueMainThread"; |
| 406 Location location(kFunction, kFile, kFakeLineNumber, NULL); |
| 407 // Do not delete birth. We don't own it. |
| 408 Births* birth = ThreadData::TallyABirthIfActive(location); |
| 409 EXPECT_EQ(birth, reinterpret_cast<Births*>(NULL)); |
| 410 |
| 411 const base::TimeTicks kTimePosted = base::TimeTicks() |
| 412 + base::TimeDelta::FromMilliseconds(1); |
| 413 const base::TimeTicks kDelayedStartTime = base::TimeTicks(); |
| 414 // TrackingInfo will call TallyABirth() during construction. |
| 415 base::TrackingInfo pending_task(location, kDelayedStartTime); |
| 416 pending_task.time_posted = kTimePosted; // Overwrite implied Now(). |
| 417 |
| 418 const TrackedTime kStartOfRun = TrackedTime() + |
| 419 Duration::FromMilliseconds(5); |
| 420 const TrackedTime kEndOfRun = TrackedTime() + Duration::FromMilliseconds(7); |
| 421 ThreadData::TallyRunOnNamedThreadIfTracking(pending_task, |
| 422 kStartOfRun, kEndOfRun); |
| 423 |
| 424 scoped_ptr<base::Value> value(ThreadData::ToValue()); |
| 425 std::string json; |
| 426 base::JSONWriter::Write(value.get(), false, &json); |
| 427 std::string one_line_result = "{" |
| 428 "\"list\":[" |
| 429 "]" |
| 430 "}"; |
| 431 EXPECT_EQ(one_line_result, json); |
| 432 } |
| 433 |
| 434 TEST_F(TrackedObjectsTest, LifeCycleToValueWorkerThread) { |
| 435 if (!ThreadData::InitializeAndSetTrackingStatus(true)) |
| 436 return; |
| 437 |
| 438 // Don't initialize thread, so that we appear as a worker thread. |
| 439 // ThreadData::InitializeThreadContext("SomeMainThreadName"); |
| 440 |
| 441 const int kFakeLineNumber = 236; |
| 442 const char* kFile = "FixedFileName"; |
| 443 const char* kFunction = "LifeCycleToValueWorkerThread"; |
| 444 Location location(kFunction, kFile, kFakeLineNumber, NULL); |
| 445 // Do not delete birth. We don't own it. |
| 446 Births* birth = ThreadData::TallyABirthIfActive(location); |
| 447 EXPECT_NE(birth, reinterpret_cast<Births*>(NULL)); |
| 448 |
| 449 const TrackedTime kTimePosted = TrackedTime() + Duration::FromMilliseconds(1); |
| 450 const TrackedTime kStartOfRun = TrackedTime() + |
| 451 Duration::FromMilliseconds(5); |
| 452 const TrackedTime kEndOfRun = TrackedTime() + Duration::FromMilliseconds(7); |
| 453 ThreadData::TallyRunOnWorkerThreadIfTracking(birth, kTimePosted, |
| 454 kStartOfRun, kEndOfRun); |
| 455 |
| 456 scoped_ptr<base::Value> value(ThreadData::ToValue()); |
| 457 std::string json; |
| 458 base::JSONWriter::Write(value.get(), false, &json); |
| 459 std::string one_line_result = "{" |
| 460 "\"list\":[" |
| 461 "{" |
| 462 "\"birth_thread\":\"WorkerThread-1\"," |
| 463 "\"death_data\":{" |
| 464 "\"count\":1," |
| 465 "\"queue_ms\":4," |
| 466 "\"run_ms\":2" |
| 467 "}," |
| 468 "\"death_thread\":\"WorkerThread-1\"," |
| 469 "\"location\":{" |
| 470 "\"file_name\":\"FixedFileName\"," |
| 471 "\"function_name\":\"LifeCycleToValueWorkerThread\"," |
| 472 "\"line_number\":236" |
| 473 "}" |
| 474 "}" |
| 475 "]" |
| 476 "}"; |
| 477 EXPECT_EQ(one_line_result, json); |
| 282 } | 478 } |
| 283 | 479 |
| 284 TEST_F(TrackedObjectsTest, TwoLives) { | 480 TEST_F(TrackedObjectsTest, TwoLives) { |
| 285 if (!ThreadData::StartTracking(true)) | 481 if (!ThreadData::InitializeAndSetTrackingStatus(true)) |
| 286 return; | 482 return; |
| 287 | 483 |
| 288 // Use a well named thread. | 484 // Use a well named thread. |
| 289 ThreadData::InitializeThreadContext("SomeFileThreadName"); | 485 ThreadData::InitializeThreadContext("SomeFileThreadName"); |
| 290 int fake_line_number = 222; | 486 const int kFakeLineNumber = 222; |
| 291 const char* kFile = "AnotherFileName"; | 487 const char* kFile = "AnotherFileName"; |
| 292 const char* kFunction = "TwoLives"; | 488 const char* kFunction = "TwoLives"; |
| 293 Location location(kFunction, kFile, fake_line_number, NULL); | 489 Location location(kFunction, kFile, kFakeLineNumber, NULL); |
| 294 // Do not delete birth. We don't own it. | 490 // Do not delete birth. We don't own it. |
| 295 Births* birth = ThreadData::TallyABirthIfActive(location); | 491 Births* birth = ThreadData::TallyABirthIfActive(location); |
| 296 EXPECT_NE(birth, reinterpret_cast<Births*>(NULL)); | 492 EXPECT_NE(birth, reinterpret_cast<Births*>(NULL)); |
| 297 | 493 |
| 298 // TimeTicks initializers ar ein microseconds. Durations are calculated in | |
| 299 // milliseconds, so we need to use 1000x. | |
| 300 const base::TimeTicks time_posted = base::TimeTicks() + | |
| 301 base::TimeDelta::FromMilliseconds(1); | |
| 302 const base::TimeTicks delayed_start_time = base::TimeTicks(); | |
| 303 const base::TimeTicks start_of_run = base::TimeTicks() + | |
| 304 base::TimeDelta::FromMilliseconds(5); | |
| 305 const base::TimeTicks end_of_run = base::TimeTicks() + | |
| 306 base::TimeDelta::FromMilliseconds(7); | |
| 307 ThreadData::TallyADeathIfActive(birth, time_posted, delayed_start_time, | |
| 308 start_of_run, end_of_run); | |
| 309 | 494 |
| 310 birth = ThreadData::TallyABirthIfActive(location); | 495 const base::TimeTicks kTimePosted = base::TimeTicks() |
| 311 ThreadData::TallyADeathIfActive(birth, time_posted, delayed_start_time, | 496 + base::TimeDelta::FromMilliseconds(1); |
| 312 start_of_run, end_of_run); | 497 const base::TimeTicks kDelayedStartTime = base::TimeTicks(); |
| 498 // TrackingInfo will call TallyABirth() during construction. |
| 499 base::TrackingInfo pending_task(location, kDelayedStartTime); |
| 500 pending_task.time_posted = kTimePosted; // Overwrite implied Now(). |
| 313 | 501 |
| 314 int process_type = 7; | 502 const TrackedTime kStartOfRun = TrackedTime() + |
| 315 scoped_ptr<base::Value> value(ThreadData::ToValue(process_type)); | 503 Duration::FromMilliseconds(5); |
| 504 const TrackedTime kEndOfRun = TrackedTime() + Duration::FromMilliseconds(7); |
| 505 ThreadData::TallyRunOnNamedThreadIfTracking(pending_task, |
| 506 kStartOfRun, kEndOfRun); |
| 507 |
| 508 // TrackingInfo will call TallyABirth() during construction. |
| 509 base::TrackingInfo pending_task2(location, kDelayedStartTime); |
| 510 pending_task2.time_posted = kTimePosted; // Overwrite implied Now(). |
| 511 |
| 512 ThreadData::TallyRunOnNamedThreadIfTracking(pending_task2, |
| 513 kStartOfRun, kEndOfRun); |
| 514 |
| 515 scoped_ptr<base::Value> value(ThreadData::ToValue()); |
| 316 std::string json; | 516 std::string json; |
| 317 base::JSONWriter::Write(value.get(), false, &json); | 517 base::JSONWriter::Write(value.get(), false, &json); |
| 318 std::string one_line_result = "{" | 518 std::string one_line_result = "{" |
| 319 "\"list\":[" | 519 "\"list\":[" |
| 320 "{" | 520 "{" |
| 321 "\"birth_thread\":\"SomeFileThreadName\"," | 521 "\"birth_thread\":\"SomeFileThreadName\"," |
| 322 "\"death_data\":{" | 522 "\"death_data\":{" |
| 323 "\"count\":2," | 523 "\"count\":2," |
| 324 "\"queue_ms\":8," | 524 "\"queue_ms\":8," |
| 325 "\"run_ms\":4" | 525 "\"run_ms\":4" |
| 326 "}," | 526 "}," |
| 327 "\"death_thread\":\"SomeFileThreadName\"," | 527 "\"death_thread\":\"SomeFileThreadName\"," |
| 328 "\"location\":{" | 528 "\"location\":{" |
| 329 "\"file_name\":\"AnotherFileName\"," | 529 "\"file_name\":\"AnotherFileName\"," |
| 330 "\"function_name\":\"TwoLives\"," | 530 "\"function_name\":\"TwoLives\"," |
| 331 "\"line_number\":222" | 531 "\"line_number\":222" |
| 332 "}" | 532 "}" |
| 333 "}" | 533 "}" |
| 334 "]," | 534 "]" |
| 335 "\"process\":7" | |
| 336 "}"; | 535 "}"; |
| 337 EXPECT_EQ(json, one_line_result); | 536 EXPECT_EQ(one_line_result, json); |
| 338 } | 537 } |
| 339 | 538 |
| 340 TEST_F(TrackedObjectsTest, DifferentLives) { | 539 TEST_F(TrackedObjectsTest, DifferentLives) { |
| 341 if (!ThreadData::StartTracking(true)) | 540 if (!ThreadData::InitializeAndSetTrackingStatus(true)) |
| 342 return; | 541 return; |
| 343 | 542 |
| 344 // Use a well named thread. | 543 // Use a well named thread. |
| 345 ThreadData::InitializeThreadContext("SomeFileThreadName"); | 544 ThreadData::InitializeThreadContext("SomeFileThreadName"); |
| 346 int fake_line_number = 567; | 545 const int kFakeLineNumber = 567; |
| 347 const char* kFile = "AnotherFileName"; | 546 const char* kFile = "AnotherFileName"; |
| 348 const char* kFunction = "DifferentLives"; | 547 const char* kFunction = "DifferentLives"; |
| 349 Location location(kFunction, kFile, fake_line_number, NULL); | 548 Location location(kFunction, kFile, kFakeLineNumber, NULL); |
| 350 // Do not delete birth. We don't own it. | |
| 351 Births* birth = ThreadData::TallyABirthIfActive(location); | |
| 352 EXPECT_NE(birth, reinterpret_cast<Births*>(NULL)); | |
| 353 | 549 |
| 354 // TimeTicks initializers ar ein microseconds. Durations are calculated in | 550 const base::TimeTicks kTimePosted = base::TimeTicks() |
| 355 // milliseconds, so we need to use 1000x. | 551 + base::TimeDelta::FromMilliseconds(1); |
| 356 const base::TimeTicks time_posted = base::TimeTicks() + | 552 const base::TimeTicks kDelayedStartTime = base::TimeTicks(); |
| 357 base::TimeDelta::FromMilliseconds(1); | 553 // TrackingInfo will call TallyABirth() during construction. |
| 358 const base::TimeTicks delayed_start_time = base::TimeTicks(); | 554 base::TrackingInfo pending_task(location, kDelayedStartTime); |
| 359 const base::TimeTicks start_of_run = base::TimeTicks() + | 555 pending_task.time_posted = kTimePosted; // Overwrite implied Now(). |
| 360 base::TimeDelta::FromMilliseconds(5); | |
| 361 const base::TimeTicks end_of_run = base::TimeTicks() + | |
| 362 base::TimeDelta::FromMilliseconds(7); | |
| 363 ThreadData::TallyADeathIfActive(birth, time_posted, delayed_start_time, | |
| 364 start_of_run, end_of_run); | |
| 365 | 556 |
| 366 int second_fake_line_number = 999; | 557 const TrackedTime kStartOfRun = TrackedTime() + |
| 367 Location second_location(kFunction, kFile, second_fake_line_number, NULL); | 558 Duration::FromMilliseconds(5); |
| 368 birth = ThreadData::TallyABirthIfActive(second_location); | 559 const TrackedTime kEndOfRun = TrackedTime() + Duration::FromMilliseconds(7); |
| 560 ThreadData::TallyRunOnNamedThreadIfTracking(pending_task, |
| 561 kStartOfRun, kEndOfRun); |
| 369 | 562 |
| 370 int process_type = 2; | 563 const int kSecondFakeLineNumber = 999; |
| 371 scoped_ptr<base::Value> value(ThreadData::ToValue(process_type)); | 564 Location second_location(kFunction, kFile, kSecondFakeLineNumber, NULL); |
| 565 |
| 566 // TrackingInfo will call TallyABirth() during construction. |
| 567 base::TrackingInfo pending_task2(second_location, kDelayedStartTime); |
| 568 pending_task2.time_posted = kTimePosted; // Overwrite implied Now(). |
| 569 |
| 570 scoped_ptr<base::Value> value(ThreadData::ToValue()); |
| 372 std::string json; | 571 std::string json; |
| 373 base::JSONWriter::Write(value.get(), false, &json); | 572 base::JSONWriter::Write(value.get(), false, &json); |
| 374 std::string one_line_result = "{" | 573 std::string one_line_result = "{" |
| 375 "\"list\":[" | 574 "\"list\":[" |
| 376 "{" | 575 "{" |
| 377 "\"birth_thread\":\"SomeFileThreadName\"," | 576 "\"birth_thread\":\"SomeFileThreadName\"," |
| 378 "\"death_data\":{" | 577 "\"death_data\":{" |
| 379 "\"count\":1," | 578 "\"count\":1," |
| 380 "\"queue_ms\":4," | 579 "\"queue_ms\":4," |
| 381 "\"run_ms\":2" | 580 "\"run_ms\":2" |
| (...skipping 12 matching lines...) Expand all Loading... |
| 394 "\"queue_ms\":0," | 593 "\"queue_ms\":0," |
| 395 "\"run_ms\":0" | 594 "\"run_ms\":0" |
| 396 "}," | 595 "}," |
| 397 "\"death_thread\":\"Still_Alive\"," | 596 "\"death_thread\":\"Still_Alive\"," |
| 398 "\"location\":{" | 597 "\"location\":{" |
| 399 "\"file_name\":\"AnotherFileName\"," | 598 "\"file_name\":\"AnotherFileName\"," |
| 400 "\"function_name\":\"DifferentLives\"," | 599 "\"function_name\":\"DifferentLives\"," |
| 401 "\"line_number\":999" | 600 "\"line_number\":999" |
| 402 "}" | 601 "}" |
| 403 "}" | 602 "}" |
| 404 "]," | 603 "]" |
| 405 "\"process\":2" | |
| 406 "}"; | 604 "}"; |
| 407 EXPECT_EQ(json, one_line_result); | 605 EXPECT_EQ(one_line_result, json); |
| 408 } | 606 } |
| 409 | 607 |
| 410 } // namespace tracked_objects | 608 } // namespace tracked_objects |
| OLD | NEW |