summaryrefslogtreecommitdiffstats
path: root/base/tracked_objects_unittest.cc
diff options
context:
space:
mode:
authorjar@chromium.org <jar@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-10-24 18:55:16 +0000
committerjar@chromium.org <jar@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-10-24 18:55:16 +0000
commit84baeca622c2d87c6a0aaf86e7a8362efe56fb25 (patch)
tree7d15d6f00ae15785e1097d2fd5a7eb8053f90b0f /base/tracked_objects_unittest.cc
parent620e0190cea3663581dd91b6f6bc61bd3c7ae02d (diff)
downloadchromium_src-84baeca622c2d87c6a0aaf86e7a8362efe56fb25.zip
chromium_src-84baeca622c2d87c6a0aaf86e7a8362efe56fb25.tar.gz
chromium_src-84baeca622c2d87c6a0aaf86e7a8362efe56fb25.tar.bz2
Support JSON encoding of data for about:tracking information
I also fought a terrible (but educational) fight with Thread Local Store, and its ability to do cleanup (call destructors) at thread exit (notably applicable to Worker Threads). Thta is why there were soooo many test bot runs and tiny checkins. I now have a plan in mind that won't rely on that functionality. The code seems to work cross-platform, but if I have trouble with Linux, I'll repeatedly leak ThreadData contexts temporarily on that platform. Given that the code is only enabled under Debug, this is not yet a real problem. Each CL I write for this code also includes a bunch of cleanup. In this case, I changed the Write() methods to WriteHTML(), since I didn't want any confusion with JSON writing etc. I also did a bunch of tiny cleanups which should not have changed what the code does. r=rtenneti Review URL: http://codereview.chromium.org/8313013 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@106952 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'base/tracked_objects_unittest.cc')
-rw-r--r--base/tracked_objects_unittest.cc322
1 files changed, 316 insertions, 6 deletions
diff --git a/base/tracked_objects_unittest.cc b/base/tracked_objects_unittest.cc
index 9c4c60a..ef88183 100644
--- a/base/tracked_objects_unittest.cc
+++ b/base/tracked_objects_unittest.cc
@@ -6,7 +6,8 @@
#include "base/tracked_objects.h"
-#include "base/message_loop.h"
+#include "base/json/json_writer.h"
+#include "base/memory/scoped_ptr.h"
#include "base/time.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -14,7 +15,10 @@ namespace tracked_objects {
class TrackedObjectsTest : public testing::Test {
public:
- MessageLoop message_loop_;
+ ~TrackedObjectsTest() {
+ ThreadData::ShutdownSingleThreadedCleanup();
+ }
+
};
TEST_F(TrackedObjectsTest, MinimalStartupShutdown) {
@@ -50,14 +54,13 @@ TEST_F(TrackedObjectsTest, MinimalStartupShutdown) {
death_map.clear();
data->SnapshotDeathMap(&death_map);
EXPECT_EQ(0u, death_map.size());
- ThreadData::ShutdownSingleThreadedCleanup();
}
TEST_F(TrackedObjectsTest, TinyStartupShutdown) {
if (!ThreadData::StartTracking(true))
return;
- // Instigate tracking on a single tracked object, or our thread.
+ // Instigate tracking on a single tracked object, on our thread.
const Location& location = FROM_HERE;
ThreadData::TallyABirthIfActive(location);
@@ -80,7 +83,8 @@ TEST_F(TrackedObjectsTest, TinyStartupShutdown) {
second_birth,
base::TimeTicks(), /* Bogus post_time. */
base::TimeTicks(), /* Bogus delayed_start_time. */
- base::TimeTicks() /* Bogus start_run_time. */);
+ base::TimeTicks(), /* Bogus start_run_time. */
+ base::TimeTicks() /* Bogus end_run_time */ );
birth_map.clear();
data->SnapshotBirthMap(&birth_map);
@@ -93,8 +97,314 @@ TEST_F(TrackedObjectsTest, TinyStartupShutdown) {
// The births were at the same location as the one known death.
EXPECT_EQ(birth_map.begin()->second, death_map.begin()->first);
+}
- ThreadData::ShutdownSingleThreadedCleanup();
+TEST_F(TrackedObjectsTest, DeathDataTest) {
+ if (!ThreadData::StartTracking(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->AverageMsRunDuration(), 0);
+ EXPECT_EQ(data->AverageMsQueueDuration(), 0);
+ EXPECT_EQ(data->count(), 0);
+
+ 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);
+ data->RecordDeath(queue_duration, run_duration);
+ EXPECT_EQ(data->run_duration(), run_duration);
+ EXPECT_EQ(data->queue_duration(), queue_duration);
+ EXPECT_EQ(data->AverageMsRunDuration(), run_ms);
+ EXPECT_EQ(data->AverageMsQueueDuration(), queue_ms);
+ EXPECT_EQ(data->count(), 1);
+
+ data->RecordDeath(queue_duration, run_duration);
+ EXPECT_EQ(data->run_duration(), run_duration + run_duration);
+ EXPECT_EQ(data->queue_duration(), queue_duration + queue_duration);
+ EXPECT_EQ(data->AverageMsRunDuration(), run_ms);
+ EXPECT_EQ(data->AverageMsQueueDuration(), 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("queue_ms", &integer));
+ EXPECT_EQ(integer, 2* queue_ms);
+ EXPECT_TRUE(dictionary->GetInteger("count", &integer));
+ EXPECT_EQ(integer, 2);
+
+ std::string output;
+ data->WriteHTML(&output);
+ std::string results = "Lives:2, Run:84ms(42ms/life) Queue:16ms(8ms/life) ";
+ EXPECT_EQ(output, results);
+
+ 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,\"run_ms\":84}";
+ EXPECT_EQ(json, birth_only_result);
+}
+
+TEST_F(TrackedObjectsTest, BirthOnlyToValueWorkerThread) {
+ if (!ThreadData::StartTracking(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 char* kFile = "FixedFileName";
+ const char* kFunction = "BirthOnlyToValueWorkerThread";
+ Location location(kFunction, kFile, fake_line_number, 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));
+ std::string json;
+ base::JSONWriter::Write(value.get(), false, &json);
+ std::string birth_only_result = "{"
+ "\"list\":["
+ "{"
+ "\"birth_thread\":\"WorkerThread-1\","
+ "\"death_data\":{"
+ "\"count\":1,"
+ "\"queue_ms\":0,"
+ "\"run_ms\":0"
+ "},"
+ "\"death_thread\":\"Still_Alive\","
+ "\"location\":{"
+ "\"file_name\":\"FixedFileName\","
+ "\"function_name\":\"BirthOnlyToValueWorkerThread\","
+ "\"line_number\":173"
+ "}"
+ "}"
+ "],"
+ "\"process\":3"
+ "}";
+ EXPECT_EQ(json, birth_only_result);
+}
+
+TEST_F(TrackedObjectsTest, BirthOnlyToValueMainThread) {
+ if (!ThreadData::StartTracking(true))
+ return;
+
+ // Use a well named thread.
+ ThreadData::InitializeThreadContext("SomeMainThreadName");
+ int fake_line_number = 173;
+ const char* kFile = "FixedFileName";
+ const char* kFunction = "BirthOnlyToValueMainThread";
+ 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));
+
+ int process_type = 34;
+ scoped_ptr<base::Value> value(ThreadData::ToValue(process_type));
+ std::string json;
+ base::JSONWriter::Write(value.get(), false, &json);
+ std::string birth_only_result = "{"
+ "\"list\":["
+ "{"
+ "\"birth_thread\":\"SomeMainThreadName\","
+ "\"death_data\":{"
+ "\"count\":1,"
+ "\"queue_ms\":0,"
+ "\"run_ms\":0"
+ "},"
+ "\"death_thread\":\"Still_Alive\","
+ "\"location\":{"
+ "\"file_name\":\"FixedFileName\","
+ "\"function_name\":\"BirthOnlyToValueMainThread\","
+ "\"line_number\":173"
+ "}"
+ "}"
+ "],"
+ "\"process\":34"
+ "}";
+ EXPECT_EQ(json, birth_only_result);
+}
+
+TEST_F(TrackedObjectsTest, LifeCycleToValueMainThread) {
+ if (!ThreadData::StartTracking(true))
+ return;
+
+ // Use a well named thread.
+ ThreadData::InitializeThreadContext("SomeMainThreadName");
+ int fake_line_number = 236;
+ const char* kFile = "FixedFileName";
+ const char* kFunction = "LifeCycleToValueMainThread";
+ 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));
+
+ // 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));
+ 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"
+ "}"
+ "}"
+ "],"
+ "\"process\":7"
+ "}";
+ EXPECT_EQ(json, one_line_result);
+}
+
+TEST_F(TrackedObjectsTest, TwoLives) {
+ if (!ThreadData::StartTracking(true))
+ return;
+
+ // Use a well named thread.
+ ThreadData::InitializeThreadContext("SomeFileThreadName");
+ int fake_line_number = 222;
+ const char* kFile = "AnotherFileName";
+ const char* kFunction = "TwoLives";
+ 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));
+
+ // 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));
+ std::string json;
+ base::JSONWriter::Write(value.get(), false, &json);
+ std::string one_line_result = "{"
+ "\"list\":["
+ "{"
+ "\"birth_thread\":\"SomeFileThreadName\","
+ "\"death_data\":{"
+ "\"count\":2,"
+ "\"queue_ms\":8,"
+ "\"run_ms\":4"
+ "},"
+ "\"death_thread\":\"SomeFileThreadName\","
+ "\"location\":{"
+ "\"file_name\":\"AnotherFileName\","
+ "\"function_name\":\"TwoLives\","
+ "\"line_number\":222"
+ "}"
+ "}"
+ "],"
+ "\"process\":7"
+ "}";
+ EXPECT_EQ(json, one_line_result);
+}
+
+TEST_F(TrackedObjectsTest, DifferentLives) {
+ if (!ThreadData::StartTracking(true))
+ return;
+
+ // Use a well named thread.
+ ThreadData::InitializeThreadContext("SomeFileThreadName");
+ int fake_line_number = 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));
+
+ // 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));
+ std::string json;
+ base::JSONWriter::Write(value.get(), false, &json);
+ std::string one_line_result = "{"
+ "\"list\":["
+ "{"
+ "\"birth_thread\":\"SomeFileThreadName\","
+ "\"death_data\":{"
+ "\"count\":1,"
+ "\"queue_ms\":4,"
+ "\"run_ms\":2"
+ "},"
+ "\"death_thread\":\"SomeFileThreadName\","
+ "\"location\":{"
+ "\"file_name\":\"AnotherFileName\","
+ "\"function_name\":\"DifferentLives\","
+ "\"line_number\":567"
+ "}"
+ "},"
+ "{"
+ "\"birth_thread\":\"SomeFileThreadName\","
+ "\"death_data\":{"
+ "\"count\":1,"
+ "\"queue_ms\":0,"
+ "\"run_ms\":0"
+ "},"
+ "\"death_thread\":\"Still_Alive\","
+ "\"location\":{"
+ "\"file_name\":\"AnotherFileName\","
+ "\"function_name\":\"DifferentLives\","
+ "\"line_number\":999"
+ "}"
+ "}"
+ "],"
+ "\"process\":2"
+ "}";
+ EXPECT_EQ(json, one_line_result);
}
} // namespace tracked_objects