| // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| // Test of classes in the tracked_objects.h classes. |
| |
| #include "base/tracked_objects.h" |
| |
| #include "base/json/json_writer.h" |
| #include "base/memory/scoped_ptr.h" |
| #include "base/time.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace tracked_objects { |
| |
| class TrackedObjectsTest : public testing::Test { |
| protected: |
| 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); |
| } |
| |
| // Provide access, since this class is a friend of ThreadData. |
| void ShutdownSingleThreadedCleanup(bool leak) { |
| ThreadData::ShutdownSingleThreadedCleanup(leak); |
| } |
| }; |
| |
| TEST_F(TrackedObjectsTest, MinimalStartupShutdown) { |
| // Minimal test doesn't even create any tasks. |
| if (!ThreadData::InitializeAndSetTrackingStatus(true)) |
| return; |
| |
| EXPECT_FALSE(ThreadData::first()); // No activity even on this thread. |
| ThreadData* data = ThreadData::Get(); |
| EXPECT_TRUE(ThreadData::first()); // Now class was constructed. |
| EXPECT_TRUE(data); |
| EXPECT_TRUE(!data->next()); |
| EXPECT_EQ(data, ThreadData::Get()); |
| ThreadData::BirthMap birth_map; |
| ThreadData::DeathMap death_map; |
| ThreadData::ParentChildSet parent_child_set; |
| data->SnapshotMaps(false, &birth_map, &death_map, &parent_child_set); |
| EXPECT_EQ(0u, birth_map.size()); |
| EXPECT_EQ(0u, death_map.size()); |
| EXPECT_EQ(0u, parent_child_set.size()); |
| // Cleanup with no leaking. |
| ShutdownSingleThreadedCleanup(false); |
| |
| // Do it again, just to be sure we reset state completely. |
| ThreadData::InitializeAndSetTrackingStatus(true); |
| EXPECT_FALSE(ThreadData::first()); // No activity even on this thread. |
| data = ThreadData::Get(); |
| EXPECT_TRUE(ThreadData::first()); // Now class was constructed. |
| EXPECT_TRUE(data); |
| EXPECT_TRUE(!data->next()); |
| EXPECT_EQ(data, ThreadData::Get()); |
| birth_map.clear(); |
| death_map.clear(); |
| data->SnapshotMaps(false, &birth_map, &death_map, &parent_child_set); |
| EXPECT_EQ(0u, birth_map.size()); |
| EXPECT_EQ(0u, death_map.size()); |
| EXPECT_EQ(0u, parent_child_set.size()); |
| } |
| |
| TEST_F(TrackedObjectsTest, TinyStartupShutdown) { |
| if (!ThreadData::InitializeAndSetTrackingStatus(true)) |
| return; |
| |
| // Instigate tracking on a single tracked object, on our thread. |
| const Location& location = FROM_HERE; |
| Births* first_birth = ThreadData::TallyABirthIfActive(location); |
| |
| ThreadData* data = ThreadData::first(); |
| ASSERT_TRUE(data); |
| EXPECT_TRUE(!data->next()); |
| EXPECT_EQ(data, ThreadData::Get()); |
| ThreadData::BirthMap birth_map; |
| ThreadData::DeathMap death_map; |
| ThreadData::ParentChildSet parent_child_set; |
| data->SnapshotMaps(false, &birth_map, &death_map, &parent_child_set); |
| EXPECT_EQ(1u, birth_map.size()); // 1 birth location. |
| EXPECT_EQ(1, birth_map.begin()->second->birth_count()); // 1 birth. |
| EXPECT_EQ(0u, death_map.size()); // No deaths. |
| EXPECT_EQ(0u, parent_child_set.size()); // No children. |
| |
| |
| // Now instigate another birth, while we are timing the run of the first |
| // execution. |
| TrackedTime start_time = |
| ThreadData::NowForStartOfRun(first_birth); |
| // Create a child (using the same birth location). |
| // TrackingInfo will call TallyABirth() during construction. |
| base::TimeTicks kBogusBirthTime; |
| base::TrackingInfo pending_task(location, kBogusBirthTime); |
| // Finally conclude the outer run. |
| TrackedTime end_time = ThreadData::NowForEndOfRun(); |
| ThreadData::TallyRunOnNamedThreadIfTracking(pending_task, start_time, |
| end_time); |
| |
| birth_map.clear(); |
| death_map.clear(); |
| parent_child_set.clear(); |
| data->SnapshotMaps(false, &birth_map, &death_map, &parent_child_set); |
| EXPECT_EQ(1u, birth_map.size()); // 1 birth location. |
| EXPECT_EQ(2, birth_map.begin()->second->birth_count()); // 2 births. |
| EXPECT_EQ(1u, death_map.size()); // 1 location. |
| EXPECT_EQ(1, death_map.begin()->second.count()); // 1 death. |
| if (ThreadData::tracking_parent_child_status()) { |
| EXPECT_EQ(1u, parent_child_set.size()); // 1 child. |
| EXPECT_EQ(parent_child_set.begin()->first, |
| parent_child_set.begin()->second); |
| } else { |
| EXPECT_EQ(0u, parent_child_set.size()); // no stats. |
| } |
| |
| // The births were at the same location as the one known death. |
| EXPECT_EQ(birth_map.begin()->second, death_map.begin()->first); |
| } |
| |
| TEST_F(TrackedObjectsTest, ParentChildTest) { |
| if (!ThreadData::InitializeAndSetTrackingStatus(true)) |
| return; |
| if (!ThreadData::tracking_parent_child_status()) |
| return; // Feature not compiled in. |
| |
| // Instigate tracking on a single tracked object, on our thread. |
| const int kFakeLineNumber = 1776; |
| const char* kFile = "FixedUnitTestFileName"; |
| const char* kFunction = "ParentChildTest"; |
| Location location(kFunction, kFile, kFakeLineNumber, NULL); |
| |
| // Now instigate another birth, while we are timing the run of the first |
| // execution. |
| |
| // Create a child (using the same birth location). |
| // TrackingInfo will call TallyABirth() during construction. |
| base::TimeTicks kBogusBirthTime; |
| base::TrackingInfo pending_task(location, kBogusBirthTime); |
| |
| // Don't conclude the run, so that we don't use the actual timer that we |
| // started for the outer profile. This way the JSON will not include some |
| // random time. |
| ThreadData* data = ThreadData::first(); |
| ASSERT_TRUE(data); |
| EXPECT_TRUE(!data->next()); |
| EXPECT_EQ(data, ThreadData::Get()); |
| |
| ThreadData::BirthMap birth_map; |
| ThreadData::DeathMap death_map; |
| ThreadData::ParentChildSet parent_child_set; |
| |
| data->SnapshotMaps(false, &birth_map, &death_map, &parent_child_set); |
| EXPECT_EQ(1u, birth_map.size()); // 1 birth location. |
| EXPECT_EQ(2, birth_map.begin()->second->birth_count()); // 2 births. |
| EXPECT_EQ(0u, death_map.size()); // No status yet. |
| // Just like TinyStartupShutdown test. |
| EXPECT_EQ(1u, parent_child_set.size()); // 1 child. |
| EXPECT_EQ(parent_child_set.begin()->first, |
| parent_child_set.begin()->second); |
| |
| scoped_ptr<base::Value> value(ThreadData::ToValue(false)); |
| std::string json; |
| base::JSONWriter::Write(value.get(), false, &json); |
| std::string birth_only_result = "{" |
| "\"descendants\":[" |
| "{" |
| "\"child_location\":{" |
| "\"file_name\":\"FixedUnitTestFileName\"," |
| "\"function_name\":\"ParentChildTest\"," |
| "\"line_number\":1776" |
| "}," |
| "\"child_thread\":\"WorkerThread-1\"," |
| "\"parent_location\":{" |
| "\"file_name\":\"FixedUnitTestFileName\"," |
| "\"function_name\":\"ParentChildTest\"," |
| "\"line_number\":1776" |
| "}," |
| "\"parent_thread\":\"WorkerThread-1\"" |
| "}" |
| "]," |
| "\"list\":[" |
| "{" |
| "\"birth_thread\":\"WorkerThread-1\"," |
| "\"death_data\":{" |
| "\"count\":2," |
| "\"queue_ms\":0," |
| "\"queue_ms_max\":0," |
| "\"queue_ms_sample\":0," |
| "\"run_ms\":0," |
| "\"run_ms_max\":0," |
| "\"run_ms_sample\":0" |
| "}," |
| "\"death_thread\":\"Still_Alive\"," |
| "\"location\":{" |
| "\"file_name\":\"FixedUnitTestFileName\"," |
| "\"function_name\":\"ParentChildTest\"," |
| "\"line_number\":1776" |
| "}" |
| "}" |
| "]" |
| "}"; |
| EXPECT_EQ(json, birth_only_result); |
| } |
| |
| TEST_F(TrackedObjectsTest, DeathDataTest) { |
| if (!ThreadData::InitializeAndSetTrackingStatus(true)) |
| return; |
| |
| scoped_ptr<DeathData> data(new DeathData()); |
| ASSERT_NE(data, reinterpret_cast<DeathData*>(NULL)); |
| EXPECT_EQ(data->run_duration_sum(), 0); |
| EXPECT_EQ(data->run_duration_sample(), 0); |
| EXPECT_EQ(data->queue_duration_sum(), 0); |
| EXPECT_EQ(data->queue_duration_sample(), 0); |
| EXPECT_EQ(data->count(), 0); |
| |
| DurationInt run_ms = 42; |
| DurationInt queue_ms = 8; |
| |
| const int kUnrandomInt = 0; // Fake random int that ensure we sample data. |
| data->RecordDeath(queue_ms, run_ms, kUnrandomInt); |
| EXPECT_EQ(data->run_duration_sum(), run_ms); |
| EXPECT_EQ(data->run_duration_sample(), run_ms); |
| EXPECT_EQ(data->queue_duration_sum(), queue_ms); |
| EXPECT_EQ(data->queue_duration_sample(), queue_ms); |
| EXPECT_EQ(data->count(), 1); |
| |
| data->RecordDeath(queue_ms, run_ms, kUnrandomInt); |
| EXPECT_EQ(data->run_duration_sum(), run_ms + run_ms); |
| EXPECT_EQ(data->run_duration_sample(), run_ms); |
| EXPECT_EQ(data->queue_duration_sum(), queue_ms + queue_ms); |
| EXPECT_EQ(data->queue_duration_sample(), queue_ms); |
| EXPECT_EQ(data->count(), 2); |
| |
| scoped_ptr<base::DictionaryValue> dictionary(data->ToValue()); |
| int integer; |
| EXPECT_TRUE(dictionary->GetInteger("run_ms", &integer)); |
| EXPECT_EQ(integer, 2 * run_ms); |
| EXPECT_TRUE(dictionary->GetInteger("run_ms_sample", &integer)); |
| EXPECT_EQ(integer, run_ms); |
| EXPECT_TRUE(dictionary->GetInteger("queue_ms", &integer)); |
| EXPECT_EQ(integer, 2 * queue_ms); |
| EXPECT_TRUE(dictionary->GetInteger("queue_ms_sample", &integer)); |
| EXPECT_EQ(integer, queue_ms); |
| EXPECT_TRUE(dictionary->GetInteger("count", &integer)); |
| EXPECT_EQ(integer, 2); |
| |
| scoped_ptr<base::Value> value(data->ToValue()); |
| std::string json; |
| base::JSONWriter::Write(value.get(), false, &json); |
| std::string birth_only_result = "{" |
| "\"count\":2," |
| "\"queue_ms\":16," |
| "\"queue_ms_max\":8," |
| "\"queue_ms_sample\":8," |
| "\"run_ms\":84," |
| "\"run_ms_max\":42," |
| "\"run_ms_sample\":42" |
| "}"; |
| EXPECT_EQ(birth_only_result, json); |
| } |
| |
| 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(false)); |
| std::string json; |
| base::JSONWriter::Write(value.get(), false, &json); |
| std::string birth_only_result = "{" |
| "\"descendants\":[" |
| "]," |
| "\"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(false)); |
| std::string json; |
| base::JSONWriter::Write(value.get(), false, &json); |
| std::string birth_only_result = "{" |
| "\"descendants\":[" |
| "]," |
| "\"list\":[" |
| "]" |
| "}"; |
| EXPECT_EQ(json, birth_only_result); |
| } |
| |
| TEST_F(TrackedObjectsTest, BirthOnlyToValueWorkerThread) { |
| if (!ThreadData::InitializeAndSetTrackingStatus(true)) |
| 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); |
| EXPECT_NE(birth, reinterpret_cast<Births*>(NULL)); |
| |
| scoped_ptr<base::Value> value(ThreadData::ToValue(false)); |
| std::string json; |
| base::JSONWriter::Write(value.get(), false, &json); |
| std::string birth_only_result = "{" |
| "\"descendants\":[" |
| "]," |
| "\"list\":[" |
| "{" |
| "\"birth_thread\":\"WorkerThread-1\"," |
| "\"death_data\":{" |
| "\"count\":1," |
| "\"queue_ms\":0," |
| "\"queue_ms_max\":0," |
| "\"queue_ms_sample\":0," |
| "\"run_ms\":0," |
| "\"run_ms_max\":0," |
| "\"run_ms_sample\":0" |
| "}," |
| "\"death_thread\":\"Still_Alive\"," |
| "\"location\":{" |
| "\"file_name\":\"FixedFileName\"," |
| "\"function_name\":\"BirthOnlyToValueWorkerThread\"," |
| "\"line_number\":173" |
| "}" |
| "}" |
| "]" |
| "}"; |
| EXPECT_EQ(json, birth_only_result); |
| } |
| |
| TEST_F(TrackedObjectsTest, BirthOnlyToValueMainThread) { |
| if (!ThreadData::InitializeAndSetTrackingStatus(true)) |
| 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); |
| EXPECT_NE(birth, reinterpret_cast<Births*>(NULL)); |
| |
| scoped_ptr<base::Value> value(ThreadData::ToValue(false)); |
| std::string json; |
| base::JSONWriter::Write(value.get(), false, &json); |
| std::string birth_only_result = "{" |
| "\"descendants\":[" |
| "]," |
| "\"list\":[" |
| "{" |
| "\"birth_thread\":\"SomeMainThreadName\"," |
| "\"death_data\":{" |
| "\"count\":1," |
| "\"queue_ms\":0," |
| "\"queue_ms_max\":0," |
| "\"queue_ms_sample\":0," |
| "\"run_ms\":0," |
| "\"run_ms_max\":0," |
| "\"run_ms_sample\":0" |
| "}," |
| "\"death_thread\":\"Still_Alive\"," |
| "\"location\":{" |
| "\"file_name\":\"FixedFileName\"," |
| "\"function_name\":\"BirthOnlyToValueMainThread\"," |
| "\"line_number\":173" |
| "}" |
| "}" |
| "]" |
| "}"; |
| EXPECT_EQ(json, birth_only_result); |
| } |
| |
| TEST_F(TrackedObjectsTest, LifeCycleToValueMainThread) { |
| 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(false)); |
| std::string json; |
| base::JSONWriter::Write(value.get(), false, &json); |
| std::string one_line_result = "{" |
| "\"descendants\":[" |
| "]," |
| "\"list\":[" |
| "{" |
| "\"birth_thread\":\"SomeMainThreadName\"," |
| "\"death_data\":{" |
| "\"count\":1," |
| "\"queue_ms\":4," |
| "\"queue_ms_max\":4," |
| "\"queue_ms_sample\":4," |
| "\"run_ms\":2," |
| "\"run_ms_max\":2," |
| "\"run_ms_sample\":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"); |
| 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(). |
| |
| // 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(false)); |
| std::string json; |
| base::JSONWriter::Write(value.get(), false, &json); |
| std::string one_line_result = "{" |
| "\"descendants\":[" |
| "]," |
| "\"list\":[" |
| "{" |
| "\"birth_thread\":\"SomeMainThreadName\"," |
| "\"death_data\":{" |
| "\"count\":1," |
| "\"queue_ms\":4," |
| "\"queue_ms_max\":4," |
| "\"queue_ms_sample\":4," |
| "\"run_ms\":2," |
| "\"run_ms_max\":2," |
| "\"run_ms_sample\":2" |
| "}," |
| "\"death_thread\":\"SomeMainThreadName\"," |
| "\"location\":{" |
| "\"file_name\":\"FixedFileName\"," |
| "\"function_name\":\"LifeCycleToValueMainThread\"," |
| "\"line_number\":236" |
| "}" |
| "}" |
| "]" |
| "}"; |
| 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(false)); |
| std::string json; |
| base::JSONWriter::Write(value.get(), false, &json); |
| std::string one_line_result = "{" |
| "\"descendants\":[" |
| "]," |
| "\"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); |
| |
| // Call for the ToValue, but tell it to not the maxes after scanning. |
| scoped_ptr<base::Value> value(ThreadData::ToValue(false)); |
| std::string json; |
| base::JSONWriter::Write(value.get(), false, &json); |
| std::string one_line_result = "{" |
| "\"descendants\":[" |
| "]," |
| "\"list\":[" |
| "{" |
| "\"birth_thread\":\"WorkerThread-1\"," |
| "\"death_data\":{" |
| "\"count\":1," |
| "\"queue_ms\":4," |
| "\"queue_ms_max\":4," |
| "\"queue_ms_sample\":4," |
| "\"run_ms\":2," |
| "\"run_ms_max\":2," |
| "\"run_ms_sample\":2" |
| "}," |
| "\"death_thread\":\"WorkerThread-1\"," |
| "\"location\":{" |
| "\"file_name\":\"FixedFileName\"," |
| "\"function_name\":\"LifeCycleToValueWorkerThread\"," |
| "\"line_number\":236" |
| "}" |
| "}" |
| "]" |
| "}"; |
| EXPECT_EQ(one_line_result, json); |
| |
| // Call for the ToValue, but tell it to reset the maxes after scanning. |
| // We'll still get the same values, but the data will be reset (which we'll |
| // see in a moment). |
| value.reset(ThreadData::ToValue(true)); |
| base::JSONWriter::Write(value.get(), false, &json); |
| // Result should be unchanged. |
| EXPECT_EQ(one_line_result, json); |
| |
| // Call for the ToValue, and now we'll see the result of the last translation, |
| // as the max will have been pushed back to zero. |
| value.reset(ThreadData::ToValue(false)); |
| base::JSONWriter::Write(value.get(), false, &json); |
| std::string one_line_result_with_zeros = "{" |
| "\"descendants\":[" |
| "]," |
| "\"list\":[" |
| "{" |
| "\"birth_thread\":\"WorkerThread-1\"," |
| "\"death_data\":{" |
| "\"count\":1," |
| "\"queue_ms\":4," |
| "\"queue_ms_max\":0," // Note zero here. |
| "\"queue_ms_sample\":4," |
| "\"run_ms\":2," |
| "\"run_ms_max\":0," // Note zero here. |
| "\"run_ms_sample\":2" |
| "}," |
| "\"death_thread\":\"WorkerThread-1\"," |
| "\"location\":{" |
| "\"file_name\":\"FixedFileName\"," |
| "\"function_name\":\"LifeCycleToValueWorkerThread\"," |
| "\"line_number\":236" |
| "}" |
| "}" |
| "]" |
| "}"; |
| EXPECT_EQ(one_line_result_with_zeros, json); |
| } |
| |
| TEST_F(TrackedObjectsTest, TwoLives) { |
| if (!ThreadData::InitializeAndSetTrackingStatus(true)) |
| return; |
| |
| // Use a well named thread. |
| ThreadData::InitializeThreadContext("SomeFileThreadName"); |
| const int kFakeLineNumber = 222; |
| const char* kFile = "AnotherFileName"; |
| const char* kFunction = "TwoLives"; |
| 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); |
| |
| // 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(false)); |
| std::string json; |
| base::JSONWriter::Write(value.get(), false, &json); |
| std::string one_line_result = "{" |
| "\"descendants\":[" |
| "]," |
| "\"list\":[" |
| "{" |
| "\"birth_thread\":\"SomeFileThreadName\"," |
| "\"death_data\":{" |
| "\"count\":2," |
| "\"queue_ms\":8," |
| "\"queue_ms_max\":4," |
| "\"queue_ms_sample\":4," |
| "\"run_ms\":4," |
| "\"run_ms_max\":2," |
| "\"run_ms_sample\":2" |
| "}," |
| "\"death_thread\":\"SomeFileThreadName\"," |
| "\"location\":{" |
| "\"file_name\":\"AnotherFileName\"," |
| "\"function_name\":\"TwoLives\"," |
| "\"line_number\":222" |
| "}" |
| "}" |
| "]" |
| "}"; |
| EXPECT_EQ(one_line_result, json); |
| } |
| |
| TEST_F(TrackedObjectsTest, DifferentLives) { |
| if (!ThreadData::InitializeAndSetTrackingStatus(true)) |
| return; |
| |
| // Use a well named thread. |
| ThreadData::InitializeThreadContext("SomeFileThreadName"); |
| const int kFakeLineNumber = 567; |
| const char* kFile = "AnotherFileName"; |
| const char* kFunction = "DifferentLives"; |
| 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(). |
| |
| scoped_ptr<base::Value> value(ThreadData::ToValue(false)); |
| std::string json; |
| base::JSONWriter::Write(value.get(), false, &json); |
| std::string one_line_result = "{" |
| "\"descendants\":[" |
| "]," |
| "\"list\":[" |
| "{" |
| "\"birth_thread\":\"SomeFileThreadName\"," |
| "\"death_data\":{" |
| "\"count\":1," |
| "\"queue_ms\":4," |
| "\"queue_ms_max\":4," |
| "\"queue_ms_sample\":4," |
| "\"run_ms\":2," |
| "\"run_ms_max\":2," |
| "\"run_ms_sample\":2" |
| "}," |
| "\"death_thread\":\"SomeFileThreadName\"," |
| "\"location\":{" |
| "\"file_name\":\"AnotherFileName\"," |
| "\"function_name\":\"DifferentLives\"," |
| "\"line_number\":567" |
| "}" |
| "}," |
| "{" |
| "\"birth_thread\":\"SomeFileThreadName\"," |
| "\"death_data\":{" |
| "\"count\":1," |
| "\"queue_ms\":0," |
| "\"queue_ms_max\":0," |
| "\"queue_ms_sample\":0," |
| "\"run_ms\":0," |
| "\"run_ms_max\":0," |
| "\"run_ms_sample\":0" |
| "}," |
| "\"death_thread\":\"Still_Alive\"," |
| "\"location\":{" |
| "\"file_name\":\"AnotherFileName\"," |
| "\"function_name\":\"DifferentLives\"," |
| "\"line_number\":999" |
| "}" |
| "}" |
| "]" |
| "}"; |
| EXPECT_EQ(one_line_result, json); |
| } |
| |
| |
| } // namespace tracked_objects |