1 /*
2 * Copyright (C) 2019 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "perfetto/ext/trace_processor/export_json.h"
18 #include "src/trace_processor/export_json.h"
19
20 #include <string.h>
21
22 #include <limits>
23
24 #include <json/reader.h>
25 #include <json/value.h>
26
27 #include "perfetto/ext/base/string_utils.h"
28 #include "perfetto/ext/base/temp_file.h"
29 #include "src/trace_processor/importers/common/args_tracker.h"
30 #include "src/trace_processor/importers/common/event_tracker.h"
31 #include "src/trace_processor/importers/common/metadata_tracker.h"
32 #include "src/trace_processor/importers/common/process_tracker.h"
33 #include "src/trace_processor/importers/common/track_tracker.h"
34 #include "src/trace_processor/importers/proto/track_event_tracker.h"
35 #include "src/trace_processor/storage/trace_storage.h"
36 #include "src/trace_processor/types/trace_processor_context.h"
37
38 #include "test/gtest_and_gmock.h"
39
40 namespace perfetto {
41 namespace trace_processor {
42 namespace json {
43 namespace {
44
ReadFile(FILE * input)45 std::string ReadFile(FILE* input) {
46 fseek(input, 0, SEEK_SET);
47 const int kBufSize = 10000;
48 char buffer[kBufSize];
49 size_t ret = fread(buffer, sizeof(char), kBufSize, input);
50 EXPECT_GT(ret, 0u);
51 return std::string(buffer, ret);
52 }
53
54 class StringOutputWriter : public OutputWriter {
55 public:
StringOutputWriter()56 StringOutputWriter() { str_.reserve(1024); }
~StringOutputWriter()57 ~StringOutputWriter() override {}
58
AppendString(const std::string & str)59 util::Status AppendString(const std::string& str) override {
60 str_ += str;
61 return util::OkStatus();
62 }
63
TakeStr()64 std::string TakeStr() { return std::move(str_); }
65
66 private:
67 std::string str_;
68 };
69
70 class ExportJsonTest : public ::testing::Test {
71 public:
ExportJsonTest()72 ExportJsonTest() {
73 context_.storage.reset(new TraceStorage());
74 context_.global_args_tracker.reset(
75 new GlobalArgsTracker(context_.storage.get()));
76 context_.args_tracker.reset(new ArgsTracker(&context_));
77 context_.event_tracker.reset(new EventTracker(&context_));
78 context_.track_tracker.reset(new TrackTracker(&context_));
79 context_.metadata_tracker.reset(
80 new MetadataTracker(context_.storage.get()));
81 context_.process_tracker.reset(new ProcessTracker(&context_));
82 }
83
ToJson(ArgumentFilterPredicate argument_filter=nullptr,MetadataFilterPredicate metadata_filter=nullptr,LabelFilterPredicate label_filter=nullptr)84 std::string ToJson(ArgumentFilterPredicate argument_filter = nullptr,
85 MetadataFilterPredicate metadata_filter = nullptr,
86 LabelFilterPredicate label_filter = nullptr) {
87 StringOutputWriter writer;
88 util::Status status =
89 ExportJson(context_.storage.get(), &writer, argument_filter,
90 metadata_filter, label_filter);
91 EXPECT_TRUE(status.ok());
92 return writer.TakeStr();
93 }
94
ToJsonValue(const std::string & json)95 Json::Value ToJsonValue(const std::string& json) {
96 Json::CharReaderBuilder b;
97 auto reader = std::unique_ptr<Json::CharReader>(b.newCharReader());
98 Json::Value result;
99 EXPECT_TRUE(reader->parse(json.data(), json.data() + json.length(), &result,
100 nullptr))
101 << json;
102 return result;
103 }
104
105 protected:
106 TraceProcessorContext context_;
107 };
108
TEST_F(ExportJsonTest,EmptyStorage)109 TEST_F(ExportJsonTest, EmptyStorage) {
110 base::TempFile temp_file = base::TempFile::Create();
111 FILE* output = fopen(temp_file.path().c_str(), "w+");
112 util::Status status = ExportJson(context_.storage.get(), output);
113
114 EXPECT_TRUE(status.ok());
115
116 Json::Value result = ToJsonValue(ReadFile(output));
117 EXPECT_EQ(result["traceEvents"].size(), 0u);
118 }
119
TEST_F(ExportJsonTest,StorageWithOneSlice)120 TEST_F(ExportJsonTest, StorageWithOneSlice) {
121 const int64_t kTimestamp = 10000000;
122 const int64_t kDuration = 10000;
123 const int64_t kThreadTimestamp = 20000000;
124 const int64_t kThreadDuration = 20000;
125 const int64_t kThreadInstructionCount = 30000000;
126 const int64_t kThreadInstructionDelta = 30000;
127 const uint32_t kThreadID = 100;
128 const char* kCategory = "cat";
129 const char* kName = "name";
130
131 UniqueTid utid = context_.process_tracker->GetOrCreateThread(kThreadID);
132 TrackId track = context_.track_tracker->InternThreadTrack(utid);
133 context_.args_tracker->Flush(); // Flush track args.
134 StringId cat_id = context_.storage->InternString(base::StringView(kCategory));
135 StringId name_id = context_.storage->InternString(base::StringView(kName));
136 // The thread_slice table is a sub table of slice.
137 context_.storage->mutable_slice_table()->Insert(
138 {kTimestamp, kDuration, track, cat_id, name_id, 0, 0, 0, SliceId(0u), 0,
139 kThreadTimestamp, kThreadDuration, kThreadInstructionCount,
140 kThreadInstructionDelta});
141
142 base::TempFile temp_file = base::TempFile::Create();
143 FILE* output = fopen(temp_file.path().c_str(), "w+");
144 util::Status status = ExportJson(context_.storage.get(), output);
145
146 EXPECT_TRUE(status.ok());
147
148 Json::Value result = ToJsonValue(ReadFile(output));
149 EXPECT_EQ(result["traceEvents"].size(), 1u);
150
151 Json::Value event = result["traceEvents"][0];
152 EXPECT_EQ(event["ph"].asString(), "X");
153 EXPECT_EQ(event["ts"].asInt64(), kTimestamp / 1000);
154 EXPECT_EQ(event["dur"].asInt64(), kDuration / 1000);
155 EXPECT_EQ(event["tts"].asInt64(), kThreadTimestamp / 1000);
156 EXPECT_EQ(event["tdur"].asInt64(), kThreadDuration / 1000);
157 EXPECT_EQ(event["ticount"].asInt64(), kThreadInstructionCount);
158 EXPECT_EQ(event["tidelta"].asInt64(), kThreadInstructionDelta);
159 EXPECT_EQ(event["tid"].asInt(), static_cast<int>(kThreadID));
160 EXPECT_EQ(event["cat"].asString(), kCategory);
161 EXPECT_EQ(event["name"].asString(), kName);
162 EXPECT_TRUE(event["args"].isObject());
163 EXPECT_EQ(event["args"].size(), 0u);
164 }
165
TEST_F(ExportJsonTest,StorageWithOneUnfinishedSlice)166 TEST_F(ExportJsonTest, StorageWithOneUnfinishedSlice) {
167 const int64_t kTimestamp = 10000000;
168 const int64_t kDuration = -1;
169 const int64_t kThreadTimestamp = 20000000;
170 const int64_t kThreadDuration = -1;
171 const int64_t kThreadInstructionCount = 30000000;
172 const int64_t kThreadInstructionDelta = -1;
173 const uint32_t kThreadID = 100;
174 const char* kCategory = "cat";
175 const char* kName = "name";
176
177 UniqueTid utid = context_.process_tracker->GetOrCreateThread(kThreadID);
178 TrackId track = context_.track_tracker->InternThreadTrack(utid);
179 context_.args_tracker->Flush(); // Flush track args.
180 StringId cat_id = context_.storage->InternString(base::StringView(kCategory));
181 StringId name_id = context_.storage->InternString(base::StringView(kName));
182 context_.storage->mutable_slice_table()->Insert(
183 {kTimestamp, kDuration, track, cat_id, name_id, 0, 0, 0, SliceId(0u), 0,
184 kThreadTimestamp, kThreadDuration, kThreadInstructionCount,
185 kThreadInstructionDelta});
186
187 base::TempFile temp_file = base::TempFile::Create();
188 FILE* output = fopen(temp_file.path().c_str(), "w+");
189 util::Status status = ExportJson(context_.storage.get(), output);
190
191 EXPECT_TRUE(status.ok());
192
193 Json::Value result = ToJsonValue(ReadFile(output));
194 EXPECT_EQ(result["traceEvents"].size(), 1u);
195
196 Json::Value event = result["traceEvents"][0];
197 EXPECT_EQ(event["ph"].asString(), "B");
198 EXPECT_EQ(event["ts"].asInt64(), kTimestamp / 1000);
199 EXPECT_FALSE(event.isMember("dur"));
200 EXPECT_EQ(event["tts"].asInt64(), kThreadTimestamp / 1000);
201 EXPECT_FALSE(event.isMember("tdur"));
202 EXPECT_EQ(event["ticount"].asInt64(), kThreadInstructionCount);
203 EXPECT_FALSE(event.isMember("tidelta"));
204 EXPECT_EQ(event["tid"].asInt(), static_cast<int>(kThreadID));
205 EXPECT_EQ(event["cat"].asString(), kCategory);
206 EXPECT_EQ(event["name"].asString(), kName);
207 EXPECT_TRUE(event["args"].isObject());
208 EXPECT_EQ(event["args"].size(), 0u);
209 }
210
TEST_F(ExportJsonTest,StorageWithThreadName)211 TEST_F(ExportJsonTest, StorageWithThreadName) {
212 const uint32_t kThreadID = 100;
213 const char* kName = "thread";
214
215 tables::ThreadTable::Row row(kThreadID);
216 row.name = context_.storage->InternString(base::StringView(kName));
217 context_.storage->mutable_thread_table()->Insert(row);
218
219 base::TempFile temp_file = base::TempFile::Create();
220 FILE* output = fopen(temp_file.path().c_str(), "w+");
221 util::Status status = ExportJson(context_.storage.get(), output);
222
223 EXPECT_TRUE(status.ok());
224
225 Json::Value result = ToJsonValue(ReadFile(output));
226 EXPECT_EQ(result["traceEvents"].size(), 1u);
227
228 Json::Value event = result["traceEvents"][0];
229 EXPECT_EQ(event["ph"].asString(), "M");
230 EXPECT_EQ(event["tid"].asInt(), static_cast<int>(kThreadID));
231 EXPECT_EQ(event["name"].asString(), "thread_name");
232 EXPECT_EQ(event["args"]["name"].asString(), kName);
233 }
234
TEST_F(ExportJsonTest,SystemEventsIgnored)235 TEST_F(ExportJsonTest, SystemEventsIgnored) {
236 TrackId track = context_.track_tracker->CreateProcessAsyncTrack(
237 /*name=*/kNullStringId, /*upid=*/0, /*source=*/kNullStringId);
238 context_.args_tracker->Flush(); // Flush track args.
239
240 // System events have no category.
241 StringId cat_id = kNullStringId;
242 StringId name_id = context_.storage->InternString("name");
243 context_.storage->mutable_slice_table()->Insert(
244 {0, 0, track, cat_id, name_id, 0, 0, 0});
245
246 base::TempFile temp_file = base::TempFile::Create();
247 FILE* output = fopen(temp_file.path().c_str(), "w+");
248 util::Status status = ExportJson(context_.storage.get(), output);
249
250 EXPECT_TRUE(status.ok());
251
252 Json::Value result = ToJsonValue(ReadFile(output));
253 EXPECT_EQ(result["traceEvents"].size(), 0u);
254 }
255
TEST_F(ExportJsonTest,StorageWithMetadata)256 TEST_F(ExportJsonTest, StorageWithMetadata) {
257 const char* kDescription = "description";
258 const char* kBenchmarkName = "benchmark name";
259 const char* kStoryName = "story name";
260 const char* kStoryTag1 = "tag1";
261 const char* kStoryTag2 = "tag2";
262 const char* kDynamicKey = "dyn_key1";
263 const int64_t kBenchmarkStart = 1000000;
264 const int64_t kStoryStart = 2000000;
265 const bool kHadFailures = true;
266
267 StringId desc_id =
268 context_.storage->InternString(base::StringView(kDescription));
269 Variadic description = Variadic::String(desc_id);
270 context_.metadata_tracker->SetMetadata(metadata::benchmark_description,
271 description);
272
273 StringId benchmark_name_id =
274 context_.storage->InternString(base::StringView(kBenchmarkName));
275 Variadic benchmark_name = Variadic::String(benchmark_name_id);
276 context_.metadata_tracker->SetMetadata(metadata::benchmark_name,
277 benchmark_name);
278
279 StringId story_name_id =
280 context_.storage->InternString(base::StringView(kStoryName));
281 Variadic story_name = Variadic::String(story_name_id);
282 context_.metadata_tracker->SetMetadata(metadata::benchmark_story_name,
283 story_name);
284
285 StringId tag1_id =
286 context_.storage->InternString(base::StringView(kStoryTag1));
287 StringId tag2_id =
288 context_.storage->InternString(base::StringView(kStoryTag2));
289 Variadic tag1 = Variadic::String(tag1_id);
290 Variadic tag2 = Variadic::String(tag2_id);
291 context_.metadata_tracker->AppendMetadata(metadata::benchmark_story_tags,
292 tag1);
293 context_.metadata_tracker->AppendMetadata(metadata::benchmark_story_tags,
294 tag2);
295
296 Variadic benchmark_start = Variadic::Integer(kBenchmarkStart);
297 context_.metadata_tracker->SetMetadata(metadata::benchmark_start_time_us,
298 benchmark_start);
299
300 Variadic story_start = Variadic::Integer(kStoryStart);
301 context_.metadata_tracker->SetMetadata(metadata::benchmark_story_run_time_us,
302 story_start);
303
304 Variadic had_failures = Variadic::Integer(kHadFailures);
305 context_.metadata_tracker->SetMetadata(metadata::benchmark_had_failures,
306 had_failures);
307
308 // Metadata entries with dynamic keys are not currently exported from the
309 // metadata table (the Chrome metadata is exported directly from the raw
310 // table).
311 StringId dynamic_key_id =
312 context_.storage->InternString(base::StringView(kDynamicKey));
313 context_.metadata_tracker->SetDynamicMetadata(dynamic_key_id, had_failures);
314
315 base::TempFile temp_file = base::TempFile::Create();
316 FILE* output = fopen(temp_file.path().c_str(), "w+");
317 util::Status status = ExportJson(context_.storage.get(), output);
318
319 EXPECT_TRUE(status.ok());
320
321 Json::Value result = ToJsonValue(ReadFile(output));
322
323 EXPECT_TRUE(result.isMember("metadata"));
324 EXPECT_TRUE(result["metadata"].isMember("telemetry"));
325 Json::Value telemetry_metadata = result["metadata"]["telemetry"];
326
327 EXPECT_EQ(telemetry_metadata["benchmarkDescriptions"].size(), 1u);
328 EXPECT_EQ(telemetry_metadata["benchmarkDescriptions"][0].asString(),
329 kDescription);
330
331 EXPECT_EQ(telemetry_metadata["benchmarks"].size(), 1u);
332 EXPECT_EQ(telemetry_metadata["benchmarks"][0].asString(), kBenchmarkName);
333
334 EXPECT_EQ(telemetry_metadata["stories"].size(), 1u);
335 EXPECT_EQ(telemetry_metadata["stories"][0].asString(), kStoryName);
336
337 EXPECT_EQ(telemetry_metadata["storyTags"].size(), 2u);
338 EXPECT_EQ(telemetry_metadata["storyTags"][0].asString(), kStoryTag1);
339 EXPECT_EQ(telemetry_metadata["storyTags"][1].asString(), kStoryTag2);
340
341 EXPECT_DOUBLE_EQ(telemetry_metadata["benchmarkStart"].asInt(),
342 kBenchmarkStart / 1000.0);
343
344 EXPECT_DOUBLE_EQ(telemetry_metadata["traceStart"].asInt(),
345 kStoryStart / 1000.0);
346
347 EXPECT_EQ(telemetry_metadata["hadFailures"].size(), 1u);
348 EXPECT_EQ(telemetry_metadata["hadFailures"][0].asBool(), kHadFailures);
349
350 EXPECT_FALSE(result["metadata"].isMember(kDynamicKey));
351 }
352
TEST_F(ExportJsonTest,StorageWithStats)353 TEST_F(ExportJsonTest, StorageWithStats) {
354 int64_t kProducers = 10;
355 int64_t kBufferSize0 = 1000;
356 int64_t kBufferSize1 = 2000;
357 int64_t kFtraceBegin = 3000;
358
359 context_.storage->SetStats(stats::traced_producers_connected, kProducers);
360 context_.storage->SetIndexedStats(stats::traced_buf_buffer_size, 0,
361 kBufferSize0);
362 context_.storage->SetIndexedStats(stats::traced_buf_buffer_size, 1,
363 kBufferSize1);
364 context_.storage->SetIndexedStats(stats::ftrace_cpu_bytes_read_begin, 0,
365 kFtraceBegin);
366
367 base::TempFile temp_file = base::TempFile::Create();
368 FILE* output = fopen(temp_file.path().c_str(), "w+");
369 util::Status status = ExportJson(context_.storage.get(), output);
370 EXPECT_TRUE(status.ok());
371
372 Json::Value result = ToJsonValue(ReadFile(output));
373
374 EXPECT_TRUE(result.isMember("metadata"));
375 EXPECT_TRUE(result["metadata"].isMember("trace_processor_stats"));
376 Json::Value stats = result["metadata"]["trace_processor_stats"];
377
378 EXPECT_EQ(stats["traced_producers_connected"].asInt(), kProducers);
379 EXPECT_EQ(stats["traced_buf"].size(), 2u);
380 EXPECT_EQ(stats["traced_buf"][0]["buffer_size"].asInt(), kBufferSize0);
381 EXPECT_EQ(stats["traced_buf"][1]["buffer_size"].asInt(), kBufferSize1);
382 EXPECT_EQ(stats["ftrace_cpu_bytes_read_begin"].size(), 1u);
383 EXPECT_EQ(stats["ftrace_cpu_bytes_read_begin"][0].asInt(), kFtraceBegin);
384 }
385
TEST_F(ExportJsonTest,StorageWithChromeMetadata)386 TEST_F(ExportJsonTest, StorageWithChromeMetadata) {
387 const char* kName1 = "name1";
388 const char* kName2 = "name2";
389 const char* kValue1 = "value1";
390 const int kValue2 = 222;
391
392 TraceStorage* storage = context_.storage.get();
393
394 RawId id =
395 storage->mutable_raw_table()
396 ->Insert({0, storage->InternString("chrome_event.metadata"), 0, 0})
397 .id;
398
399 StringId name1_id = storage->InternString(base::StringView(kName1));
400 StringId name2_id = storage->InternString(base::StringView(kName2));
401 StringId value1_id = storage->InternString(base::StringView(kValue1));
402
403 context_.args_tracker->AddArgsTo(id)
404 .AddArg(name1_id, Variadic::String(value1_id))
405 .AddArg(name2_id, Variadic::Integer(kValue2));
406 context_.args_tracker->Flush();
407
408 base::TempFile temp_file = base::TempFile::Create();
409 FILE* output = fopen(temp_file.path().c_str(), "w+");
410 util::Status status = ExportJson(storage, output);
411 EXPECT_TRUE(status.ok());
412
413 Json::Value result = ToJsonValue(ReadFile(output));
414
415 EXPECT_TRUE(result.isMember("metadata"));
416 Json::Value metadata = result["metadata"];
417
418 EXPECT_EQ(metadata[kName1].asString(), kValue1);
419 EXPECT_EQ(metadata[kName2].asInt(), kValue2);
420 }
421
TEST_F(ExportJsonTest,StorageWithArgs)422 TEST_F(ExportJsonTest, StorageWithArgs) {
423 const char* kCategory = "cat";
424 const char* kName = "name";
425 const char* kSrc = "source_file.cc";
426
427 UniqueTid utid = context_.process_tracker->GetOrCreateThread(0);
428 TrackId track = context_.track_tracker->InternThreadTrack(utid);
429 context_.args_tracker->Flush(); // Flush track args.
430 StringId cat_id = context_.storage->InternString(base::StringView(kCategory));
431 StringId name_id = context_.storage->InternString(base::StringView(kName));
432 context_.storage->mutable_slice_table()->Insert(
433 {0, 0, track, cat_id, name_id, 0, 0, 0});
434
435 StringId arg_key_id = context_.storage->InternString(
436 base::StringView("task.posted_from.file_name"));
437 StringId arg_value_id =
438 context_.storage->InternString(base::StringView(kSrc));
439 GlobalArgsTracker::Arg arg;
440 arg.flat_key = arg_key_id;
441 arg.key = arg_key_id;
442 arg.value = Variadic::String(arg_value_id);
443 ArgSetId args = context_.global_args_tracker->AddArgSet({arg}, 0, 1);
444 context_.storage->mutable_slice_table()->mutable_arg_set_id()->Set(0, args);
445
446 base::TempFile temp_file = base::TempFile::Create();
447 FILE* output = fopen(temp_file.path().c_str(), "w+");
448 util::Status status = ExportJson(context_.storage.get(), output);
449
450 EXPECT_TRUE(status.ok());
451
452 Json::Value result = ToJsonValue(ReadFile(output));
453 EXPECT_EQ(result["traceEvents"].size(), 1u);
454
455 Json::Value event = result["traceEvents"][0];
456 EXPECT_EQ(event["cat"].asString(), kCategory);
457 EXPECT_EQ(event["name"].asString(), kName);
458 EXPECT_EQ(event["args"]["src"].asString(), kSrc);
459 }
460
TEST_F(ExportJsonTest,StorageWithSliceAndFlowEventArgs)461 TEST_F(ExportJsonTest, StorageWithSliceAndFlowEventArgs) {
462 const char* kCategory = "cat";
463 const char* kName = "name";
464
465 TraceStorage* storage = context_.storage.get();
466
467 UniqueTid utid = context_.process_tracker->GetOrCreateThread(0);
468 TrackId track = context_.track_tracker->InternThreadTrack(utid);
469 context_.args_tracker->Flush(); // Flush track args.
470 StringId cat_id = storage->InternString(base::StringView(kCategory));
471 StringId name_id = storage->InternString(base::StringView(kName));
472 SliceId id1 = storage->mutable_slice_table()
473 ->Insert({0, 0, track, cat_id, name_id, 0, 0, 0})
474 .id;
475 SliceId id2 = storage->mutable_slice_table()
476 ->Insert({100, 0, track, cat_id, name_id, 0, 0, 0})
477 .id;
478
479 storage->mutable_flow_table()->Insert({id1, id2, 0});
480
481 base::TempFile temp_file = base::TempFile::Create();
482 FILE* output = fopen(temp_file.path().c_str(), "w+");
483 util::Status status = ExportJson(storage, output);
484
485 EXPECT_TRUE(status.ok());
486
487 Json::Value result = ToJsonValue(ReadFile(output));
488 EXPECT_EQ(result["traceEvents"].size(), 4u);
489
490 Json::Value slice_out = result["traceEvents"][0];
491 Json::Value slice_in = result["traceEvents"][1];
492 Json::Value flow_out = result["traceEvents"][2];
493 Json::Value flow_in = result["traceEvents"][3];
494
495 EXPECT_EQ(flow_out["cat"].asString(), kCategory);
496 EXPECT_EQ(flow_out["name"].asString(), kName);
497 EXPECT_EQ(flow_out["ph"].asString(), "s");
498 EXPECT_EQ(flow_out["tid"].asString(), slice_out["tid"].asString());
499 EXPECT_EQ(flow_out["pid"].asString(), slice_out["pid"].asString());
500
501 EXPECT_EQ(flow_in["cat"].asString(), kCategory);
502 EXPECT_EQ(flow_in["name"].asString(), kName);
503 EXPECT_EQ(flow_in["ph"].asString(), "f");
504 EXPECT_EQ(flow_in["bp"].asString(), "e");
505 EXPECT_EQ(flow_in["tid"].asString(), slice_in["tid"].asString());
506 EXPECT_EQ(flow_in["pid"].asString(), slice_in["pid"].asString());
507
508 EXPECT_LE(slice_out["ts"].asInt64(), flow_out["ts"].asInt64());
509 EXPECT_GE(slice_in["ts"].asInt64(), flow_in["ts"].asInt64());
510
511 EXPECT_EQ(flow_out["id"].asString(), flow_in["id"].asString());
512 }
513
TEST_F(ExportJsonTest,StorageWithListArgs)514 TEST_F(ExportJsonTest, StorageWithListArgs) {
515 const char* kCategory = "cat";
516 const char* kName = "name";
517 double kValues[] = {1.234, 2.345};
518
519 UniqueTid utid = context_.process_tracker->GetOrCreateThread(0);
520 TrackId track = context_.track_tracker->InternThreadTrack(utid);
521 context_.args_tracker->Flush(); // Flush track args.
522 StringId cat_id = context_.storage->InternString(base::StringView(kCategory));
523 StringId name_id = context_.storage->InternString(base::StringView(kName));
524 context_.storage->mutable_slice_table()->Insert(
525 {0, 0, track, cat_id, name_id, 0, 0, 0});
526
527 StringId arg_flat_key_id = context_.storage->InternString(
528 base::StringView("debug.draw_duration_ms"));
529 StringId arg_key0_id = context_.storage->InternString(
530 base::StringView("debug.draw_duration_ms[0]"));
531 StringId arg_key1_id = context_.storage->InternString(
532 base::StringView("debug.draw_duration_ms[1]"));
533 GlobalArgsTracker::Arg arg0;
534 arg0.flat_key = arg_flat_key_id;
535 arg0.key = arg_key0_id;
536 arg0.value = Variadic::Real(kValues[0]);
537 GlobalArgsTracker::Arg arg1;
538 arg1.flat_key = arg_flat_key_id;
539 arg1.key = arg_key1_id;
540 arg1.value = Variadic::Real(kValues[1]);
541 ArgSetId args = context_.global_args_tracker->AddArgSet({arg0, arg1}, 0, 2);
542 context_.storage->mutable_slice_table()->mutable_arg_set_id()->Set(0, args);
543
544 base::TempFile temp_file = base::TempFile::Create();
545 FILE* output = fopen(temp_file.path().c_str(), "w+");
546 util::Status status = ExportJson(context_.storage.get(), output);
547
548 EXPECT_TRUE(status.ok());
549
550 Json::Value result = ToJsonValue(ReadFile(output));
551 EXPECT_EQ(result["traceEvents"].size(), 1u);
552
553 Json::Value event = result["traceEvents"][0];
554 EXPECT_EQ(event["cat"].asString(), kCategory);
555 EXPECT_EQ(event["name"].asString(), kName);
556 EXPECT_EQ(event["args"]["draw_duration_ms"].size(), 2u);
557 EXPECT_DOUBLE_EQ(event["args"]["draw_duration_ms"][0].asDouble(), kValues[0]);
558 EXPECT_DOUBLE_EQ(event["args"]["draw_duration_ms"][1].asDouble(), kValues[1]);
559 }
560
TEST_F(ExportJsonTest,StorageWithMultiplePointerArgs)561 TEST_F(ExportJsonTest, StorageWithMultiplePointerArgs) {
562 const char* kCategory = "cat";
563 const char* kName = "name";
564 uint64_t kValue0 = 1;
565 uint64_t kValue1 = std::numeric_limits<uint64_t>::max();
566
567 UniqueTid utid = context_.process_tracker->GetOrCreateThread(0);
568 TrackId track = context_.track_tracker->InternThreadTrack(utid);
569 context_.args_tracker->Flush(); // Flush track args.
570 StringId cat_id = context_.storage->InternString(base::StringView(kCategory));
571 StringId name_id = context_.storage->InternString(base::StringView(kName));
572 context_.storage->mutable_slice_table()->Insert(
573 {0, 0, track, cat_id, name_id, 0, 0, 0});
574
575 StringId arg_key0_id =
576 context_.storage->InternString(base::StringView("arg0"));
577 StringId arg_key1_id =
578 context_.storage->InternString(base::StringView("arg1"));
579 GlobalArgsTracker::Arg arg0;
580 arg0.flat_key = arg_key0_id;
581 arg0.key = arg_key0_id;
582 arg0.value = Variadic::Pointer(kValue0);
583 GlobalArgsTracker::Arg arg1;
584 arg1.flat_key = arg_key1_id;
585 arg1.key = arg_key1_id;
586 arg1.value = Variadic::Pointer(kValue1);
587 ArgSetId args = context_.global_args_tracker->AddArgSet({arg0, arg1}, 0, 2);
588 context_.storage->mutable_slice_table()->mutable_arg_set_id()->Set(0, args);
589
590 base::TempFile temp_file = base::TempFile::Create();
591 FILE* output = fopen(temp_file.path().c_str(), "w+");
592 util::Status status = ExportJson(context_.storage.get(), output);
593
594 EXPECT_TRUE(status.ok());
595
596 Json::Value result = ToJsonValue(ReadFile(output));
597 EXPECT_EQ(result["traceEvents"].size(), 1u);
598
599 Json::Value event = result["traceEvents"][0];
600 EXPECT_EQ(event["cat"].asString(), kCategory);
601 EXPECT_EQ(event["name"].asString(), kName);
602 EXPECT_EQ(event["args"]["arg0"].asString(), "0x1");
603 EXPECT_EQ(event["args"]["arg1"].asString(), "0xffffffffffffffff");
604 }
605
TEST_F(ExportJsonTest,StorageWithObjectListArgs)606 TEST_F(ExportJsonTest, StorageWithObjectListArgs) {
607 const char* kCategory = "cat";
608 const char* kName = "name";
609 int kValues[] = {123, 234};
610
611 UniqueTid utid = context_.process_tracker->GetOrCreateThread(0);
612 TrackId track = context_.track_tracker->InternThreadTrack(utid);
613 context_.args_tracker->Flush(); // Flush track args.
614 StringId cat_id = context_.storage->InternString(base::StringView(kCategory));
615 StringId name_id = context_.storage->InternString(base::StringView(kName));
616 context_.storage->mutable_slice_table()->Insert(
617 {0, 0, track, cat_id, name_id, 0, 0, 0});
618
619 StringId arg_flat_key_id =
620 context_.storage->InternString(base::StringView("a.b"));
621 StringId arg_key0_id =
622 context_.storage->InternString(base::StringView("a[0].b"));
623 StringId arg_key1_id =
624 context_.storage->InternString(base::StringView("a[1].b"));
625 GlobalArgsTracker::Arg arg0;
626 arg0.flat_key = arg_flat_key_id;
627 arg0.key = arg_key0_id;
628 arg0.value = Variadic::Integer(kValues[0]);
629 GlobalArgsTracker::Arg arg1;
630 arg1.flat_key = arg_flat_key_id;
631 arg1.key = arg_key1_id;
632 arg1.value = Variadic::Integer(kValues[1]);
633 ArgSetId args = context_.global_args_tracker->AddArgSet({arg0, arg1}, 0, 2);
634 context_.storage->mutable_slice_table()->mutable_arg_set_id()->Set(0, args);
635
636 base::TempFile temp_file = base::TempFile::Create();
637 FILE* output = fopen(temp_file.path().c_str(), "w+");
638 util::Status status = ExportJson(context_.storage.get(), output);
639
640 EXPECT_TRUE(status.ok());
641
642 Json::Value result = ToJsonValue(ReadFile(output));
643 EXPECT_EQ(result["traceEvents"].size(), 1u);
644
645 Json::Value event = result["traceEvents"][0];
646 EXPECT_EQ(event["cat"].asString(), kCategory);
647 EXPECT_EQ(event["name"].asString(), kName);
648 EXPECT_EQ(event["args"]["a"].size(), 2u);
649 EXPECT_EQ(event["args"]["a"][0]["b"].asInt(), kValues[0]);
650 EXPECT_EQ(event["args"]["a"][1]["b"].asInt(), kValues[1]);
651 }
652
TEST_F(ExportJsonTest,StorageWithNestedListArgs)653 TEST_F(ExportJsonTest, StorageWithNestedListArgs) {
654 const char* kCategory = "cat";
655 const char* kName = "name";
656 int kValues[] = {123, 234};
657
658 UniqueTid utid = context_.process_tracker->GetOrCreateThread(0);
659 TrackId track = context_.track_tracker->InternThreadTrack(utid);
660 context_.args_tracker->Flush(); // Flush track args.
661 StringId cat_id = context_.storage->InternString(base::StringView(kCategory));
662 StringId name_id = context_.storage->InternString(base::StringView(kName));
663 context_.storage->mutable_slice_table()->Insert(
664 {0, 0, track, cat_id, name_id, 0, 0, 0});
665
666 StringId arg_flat_key_id =
667 context_.storage->InternString(base::StringView("a"));
668 StringId arg_key0_id =
669 context_.storage->InternString(base::StringView("a[0][0]"));
670 StringId arg_key1_id =
671 context_.storage->InternString(base::StringView("a[0][1]"));
672 GlobalArgsTracker::Arg arg0;
673 arg0.flat_key = arg_flat_key_id;
674 arg0.key = arg_key0_id;
675 arg0.value = Variadic::Integer(kValues[0]);
676 GlobalArgsTracker::Arg arg1;
677 arg1.flat_key = arg_flat_key_id;
678 arg1.key = arg_key1_id;
679 arg1.value = Variadic::Integer(kValues[1]);
680 ArgSetId args = context_.global_args_tracker->AddArgSet({arg0, arg1}, 0, 2);
681 context_.storage->mutable_slice_table()->mutable_arg_set_id()->Set(0, args);
682
683 base::TempFile temp_file = base::TempFile::Create();
684 FILE* output = fopen(temp_file.path().c_str(), "w+");
685 util::Status status = ExportJson(context_.storage.get(), output);
686
687 EXPECT_TRUE(status.ok());
688
689 Json::Value result = ToJsonValue(ReadFile(output));
690 EXPECT_EQ(result["traceEvents"].size(), 1u);
691
692 Json::Value event = result["traceEvents"][0];
693 EXPECT_EQ(event["cat"].asString(), kCategory);
694 EXPECT_EQ(event["name"].asString(), kName);
695 EXPECT_EQ(event["args"]["a"].size(), 1u);
696 EXPECT_EQ(event["args"]["a"][0].size(), 2u);
697 EXPECT_EQ(event["args"]["a"][0][0].asInt(), kValues[0]);
698 EXPECT_EQ(event["args"]["a"][0][1].asInt(), kValues[1]);
699 }
700
TEST_F(ExportJsonTest,StorageWithLegacyJsonArgs)701 TEST_F(ExportJsonTest, StorageWithLegacyJsonArgs) {
702 const char* kCategory = "cat";
703 const char* kName = "name";
704
705 UniqueTid utid = context_.process_tracker->GetOrCreateThread(0);
706 TrackId track = context_.track_tracker->InternThreadTrack(utid);
707 context_.args_tracker->Flush(); // Flush track args.
708 StringId cat_id = context_.storage->InternString(base::StringView(kCategory));
709 StringId name_id = context_.storage->InternString(base::StringView(kName));
710 context_.storage->mutable_slice_table()->Insert(
711 {0, 0, track, cat_id, name_id, 0, 0, 0});
712
713 StringId arg_key_id = context_.storage->InternString(base::StringView("a"));
714 StringId arg_value_id =
715 context_.storage->InternString(base::StringView("{\"b\":123}"));
716 GlobalArgsTracker::Arg arg;
717 arg.flat_key = arg_key_id;
718 arg.key = arg_key_id;
719 arg.value = Variadic::Json(arg_value_id);
720 ArgSetId args = context_.global_args_tracker->AddArgSet({arg}, 0, 1);
721 context_.storage->mutable_slice_table()->mutable_arg_set_id()->Set(0, args);
722
723 base::TempFile temp_file = base::TempFile::Create();
724 FILE* output = fopen(temp_file.path().c_str(), "w+");
725 util::Status status = ExportJson(context_.storage.get(), output);
726
727 EXPECT_TRUE(status.ok());
728
729 Json::Value result = ToJsonValue(ReadFile(output));
730 EXPECT_EQ(result["traceEvents"].size(), 1u);
731
732 Json::Value event = result["traceEvents"][0];
733 EXPECT_EQ(event["cat"].asString(), kCategory);
734 EXPECT_EQ(event["name"].asString(), kName);
735 EXPECT_EQ(event["args"]["a"]["b"].asInt(), 123);
736 }
737
TEST_F(ExportJsonTest,InstantEvent)738 TEST_F(ExportJsonTest, InstantEvent) {
739 const int64_t kTimestamp = 10000000;
740 const int64_t kTimestamp2 = 10001000;
741 const int64_t kTimestamp3 = 10002000;
742 const char* kCategory = "cat";
743 const char* kName = "name";
744
745 // Global legacy track.
746 TrackId track =
747 context_.track_tracker->GetOrCreateLegacyChromeGlobalInstantTrack();
748 context_.args_tracker->Flush(); // Flush track args.
749 StringId cat_id = context_.storage->InternString(base::StringView(kCategory));
750 StringId name_id = context_.storage->InternString(base::StringView(kName));
751 context_.storage->mutable_slice_table()->Insert(
752 {kTimestamp, 0, track, cat_id, name_id, 0, 0, 0});
753
754 // Global track.
755 TrackEventTracker track_event_tracker(&context_);
756 TrackId track2 = track_event_tracker.GetOrCreateDefaultDescriptorTrack();
757 context_.args_tracker->Flush(); // Flush track args.
758 context_.storage->mutable_slice_table()->Insert(
759 {kTimestamp2, 0, track2, cat_id, name_id, 0, 0, 0});
760
761 // Async event track.
762 track_event_tracker.ReserveDescriptorChildTrack(1234, 0, kNullStringId);
763 TrackId track3 = *track_event_tracker.GetDescriptorTrack(1234);
764 context_.args_tracker->Flush(); // Flush track args.
765 context_.storage->mutable_slice_table()->Insert(
766 {kTimestamp3, 0, track3, cat_id, name_id, 0, 0, 0});
767
768 base::TempFile temp_file = base::TempFile::Create();
769 FILE* output = fopen(temp_file.path().c_str(), "w+");
770 util::Status status = ExportJson(context_.storage.get(), output);
771
772 EXPECT_TRUE(status.ok());
773
774 Json::Value result = ToJsonValue(ReadFile(output));
775 EXPECT_EQ(result["traceEvents"].size(), 3u);
776
777 Json::Value event = result["traceEvents"][0];
778 EXPECT_EQ(event["ph"].asString(), "I");
779 EXPECT_EQ(event["ts"].asInt64(), kTimestamp / 1000);
780 EXPECT_EQ(event["s"].asString(), "g");
781 EXPECT_EQ(event["cat"].asString(), kCategory);
782 EXPECT_EQ(event["name"].asString(), kName);
783
784 Json::Value event2 = result["traceEvents"][1];
785 EXPECT_EQ(event2["ph"].asString(), "I");
786 EXPECT_EQ(event2["ts"].asInt64(), kTimestamp2 / 1000);
787 EXPECT_EQ(event2["s"].asString(), "g");
788 EXPECT_EQ(event2["cat"].asString(), kCategory);
789 EXPECT_EQ(event2["name"].asString(), kName);
790
791 Json::Value event3 = result["traceEvents"][2];
792 EXPECT_EQ(event3["ph"].asString(), "n");
793 EXPECT_EQ(event3["ts"].asInt64(), kTimestamp3 / 1000);
794 EXPECT_EQ(event3["id"].asString(), "0x2");
795 EXPECT_EQ(event3["cat"].asString(), kCategory);
796 EXPECT_EQ(event3["name"].asString(), kName);
797 }
798
TEST_F(ExportJsonTest,InstantEventOnThread)799 TEST_F(ExportJsonTest, InstantEventOnThread) {
800 const int64_t kTimestamp = 10000000;
801 const uint32_t kThreadID = 100;
802 const char* kCategory = "cat";
803 const char* kName = "name";
804
805 UniqueTid utid = context_.process_tracker->GetOrCreateThread(kThreadID);
806 TrackId track = context_.track_tracker->InternThreadTrack(utid);
807 context_.args_tracker->Flush(); // Flush track args.
808 StringId cat_id = context_.storage->InternString(base::StringView(kCategory));
809 StringId name_id = context_.storage->InternString(base::StringView(kName));
810 context_.storage->mutable_slice_table()->Insert(
811 {kTimestamp, 0, track, cat_id, name_id, 0, 0, 0});
812
813 base::TempFile temp_file = base::TempFile::Create();
814 FILE* output = fopen(temp_file.path().c_str(), "w+");
815 util::Status status = ExportJson(context_.storage.get(), output);
816
817 EXPECT_TRUE(status.ok());
818
819 Json::Value result = ToJsonValue(ReadFile(output));
820 EXPECT_EQ(result["traceEvents"].size(), 1u);
821
822 Json::Value event = result["traceEvents"][0];
823 EXPECT_EQ(event["tid"].asInt(), static_cast<int>(kThreadID));
824 EXPECT_EQ(event["ph"].asString(), "I");
825 EXPECT_EQ(event["ts"].asInt64(), kTimestamp / 1000);
826 EXPECT_EQ(event["s"].asString(), "t");
827 EXPECT_EQ(event["cat"].asString(), kCategory);
828 EXPECT_EQ(event["name"].asString(), kName);
829 }
830
TEST_F(ExportJsonTest,DuplicatePidAndTid)831 TEST_F(ExportJsonTest, DuplicatePidAndTid) {
832 UniqueTid upid1 = context_.process_tracker->StartNewProcess(
833 std::nullopt, std::nullopt, 1, kNullStringId,
834 ThreadNamePriority::kTrackDescriptor);
835 UniqueTid utid1a = context_.process_tracker->UpdateThread(1, 1);
836 UniqueTid utid1b = context_.process_tracker->UpdateThread(2, 1);
837 UniqueTid utid1c = context_.process_tracker->StartNewThread(std::nullopt, 2);
838 // Associate the new thread with its process.
839 ASSERT_EQ(utid1c, context_.process_tracker->UpdateThread(2, 1));
840
841 UniqueTid upid2 = context_.process_tracker->StartNewProcess(
842 std::nullopt, std::nullopt, 1, kNullStringId,
843 ThreadNamePriority::kTrackDescriptor);
844 UniqueTid utid2a = context_.process_tracker->UpdateThread(1, 1);
845 UniqueTid utid2b = context_.process_tracker->UpdateThread(2, 1);
846
847 ASSERT_NE(upid1, upid2);
848 ASSERT_NE(utid1b, utid1c);
849 ASSERT_NE(utid1a, utid2a);
850 ASSERT_NE(utid1b, utid2b);
851 ASSERT_NE(utid1c, utid2b);
852
853 ASSERT_EQ(upid1, *context_.storage->thread_table().upid()[utid1a]);
854 ASSERT_EQ(upid1, *context_.storage->thread_table().upid()[utid1b]);
855 ASSERT_EQ(upid1, *context_.storage->thread_table().upid()[utid1c]);
856 ASSERT_EQ(upid2, *context_.storage->thread_table().upid()[utid2a]);
857 ASSERT_EQ(upid2, *context_.storage->thread_table().upid()[utid2b]);
858
859 TrackId track1a = context_.track_tracker->InternThreadTrack(utid1a);
860 TrackId track1b = context_.track_tracker->InternThreadTrack(utid1b);
861 TrackId track1c = context_.track_tracker->InternThreadTrack(utid1c);
862 TrackId track2a = context_.track_tracker->InternThreadTrack(utid2a);
863 TrackId track2b = context_.track_tracker->InternThreadTrack(utid2b);
864 context_.args_tracker->Flush(); // Flush track args.
865
866 StringId cat_id = context_.storage->InternString(base::StringView("cat"));
867 StringId name1a_id =
868 context_.storage->InternString(base::StringView("name1a"));
869 StringId name1b_id =
870 context_.storage->InternString(base::StringView("name1b"));
871 StringId name1c_id =
872 context_.storage->InternString(base::StringView("name1c"));
873 StringId name2a_id =
874 context_.storage->InternString(base::StringView("name2a"));
875 StringId name2b_id =
876 context_.storage->InternString(base::StringView("name2b"));
877
878 context_.storage->mutable_slice_table()->Insert(
879 {10000, 0, track1a, cat_id, name1a_id, 0, 0, 0});
880 context_.storage->mutable_slice_table()->Insert(
881 {20000, 1000, track1b, cat_id, name1b_id, 0, 0, 0});
882 context_.storage->mutable_slice_table()->Insert(
883 {30000, 0, track1c, cat_id, name1c_id, 0, 0, 0});
884 context_.storage->mutable_slice_table()->Insert(
885 {40000, 0, track2a, cat_id, name2a_id, 0, 0, 0});
886 context_.storage->mutable_slice_table()->Insert(
887 {50000, 1000, track2b, cat_id, name2b_id, 0, 0, 0});
888
889 base::TempFile temp_file = base::TempFile::Create();
890 FILE* output = fopen(temp_file.path().c_str(), "w+");
891 util::Status status = ExportJson(context_.storage.get(), output);
892
893 EXPECT_TRUE(status.ok());
894
895 Json::Value result = ToJsonValue(ReadFile(output));
896 EXPECT_EQ(result["traceEvents"].size(), 5u);
897
898 EXPECT_EQ(result["traceEvents"][0]["pid"].asInt(), 1);
899 EXPECT_EQ(result["traceEvents"][0]["tid"].asInt(), 1);
900 EXPECT_EQ(result["traceEvents"][0]["ph"].asString(), "I");
901 EXPECT_EQ(result["traceEvents"][0]["ts"].asInt64(), 10);
902 EXPECT_EQ(result["traceEvents"][0]["cat"].asString(), "cat");
903 EXPECT_EQ(result["traceEvents"][0]["name"].asString(), "name1a");
904
905 EXPECT_EQ(result["traceEvents"][1]["pid"].asInt(), 1);
906 EXPECT_EQ(result["traceEvents"][1]["tid"].asInt(), 2);
907 EXPECT_EQ(result["traceEvents"][1]["ph"].asString(), "X");
908 EXPECT_EQ(result["traceEvents"][1]["ts"].asInt64(), 20);
909 EXPECT_EQ(result["traceEvents"][1]["dur"].asInt64(), 1);
910 EXPECT_EQ(result["traceEvents"][1]["cat"].asString(), "cat");
911 EXPECT_EQ(result["traceEvents"][1]["name"].asString(), "name1b");
912
913 EXPECT_EQ(result["traceEvents"][2]["pid"].asInt(), 1);
914 EXPECT_EQ(result["traceEvents"][2]["tid"].asInt(),
915 static_cast<int>(std::numeric_limits<uint32_t>::max() - 1u));
916 EXPECT_EQ(result["traceEvents"][2]["ph"].asString(), "I");
917 EXPECT_EQ(result["traceEvents"][2]["ts"].asInt64(), 30);
918 EXPECT_EQ(result["traceEvents"][2]["cat"].asString(), "cat");
919 EXPECT_EQ(result["traceEvents"][2]["name"].asString(), "name1c");
920
921 EXPECT_EQ(result["traceEvents"][3]["pid"].asInt(),
922 static_cast<int>(std::numeric_limits<uint32_t>::max()));
923 EXPECT_EQ(result["traceEvents"][3]["tid"].asInt(), 1);
924 EXPECT_EQ(result["traceEvents"][3]["ph"].asString(), "I");
925 EXPECT_EQ(result["traceEvents"][3]["ts"].asInt64(), 40);
926 EXPECT_EQ(result["traceEvents"][3]["cat"].asString(), "cat");
927 EXPECT_EQ(result["traceEvents"][3]["name"].asString(), "name2a");
928
929 EXPECT_EQ(result["traceEvents"][4]["pid"].asInt(),
930 static_cast<int>(std::numeric_limits<uint32_t>::max()));
931 EXPECT_EQ(result["traceEvents"][4]["tid"].asInt(), 2);
932 EXPECT_EQ(result["traceEvents"][4]["ph"].asString(), "X");
933 EXPECT_EQ(result["traceEvents"][4]["ts"].asInt64(), 50);
934 EXPECT_EQ(result["traceEvents"][1]["dur"].asInt64(), 1);
935 EXPECT_EQ(result["traceEvents"][4]["cat"].asString(), "cat");
936 EXPECT_EQ(result["traceEvents"][4]["name"].asString(), "name2b");
937 }
938
TEST_F(ExportJsonTest,AsyncEvents)939 TEST_F(ExportJsonTest, AsyncEvents) {
940 const int64_t kTimestamp = 10000000;
941 const int64_t kDuration = 100000;
942 const int64_t kTimestamp3 = 10005000;
943 const int64_t kDuration3 = 100000;
944 const uint32_t kProcessID = 100;
945 const char* kCategory = "cat";
946 const char* kName = "name";
947 const char* kName2 = "name2";
948 const char* kName3 = "name3";
949 const char* kArgName = "arg_name";
950 const int kArgValue = 123;
951
952 UniquePid upid = context_.process_tracker->GetOrCreateProcess(kProcessID);
953 StringId cat_id = context_.storage->InternString(base::StringView(kCategory));
954 StringId name_id = context_.storage->InternString(base::StringView(kName));
955 StringId name2_id = context_.storage->InternString(base::StringView(kName2));
956 StringId name3_id = context_.storage->InternString(base::StringView(kName3));
957
958 constexpr int64_t kSourceId = 235;
959 TrackId track = context_.track_tracker->InternLegacyChromeAsyncTrack(
960 name_id, upid, kSourceId, /*source_id_is_process_scoped=*/true,
961 /*source_scope=*/kNullStringId);
962 constexpr int64_t kSourceId2 = 236;
963 TrackId track2 = context_.track_tracker->InternLegacyChromeAsyncTrack(
964 name3_id, upid, kSourceId2, /*source_id_is_process_scoped=*/true,
965 /*source_scope=*/kNullStringId);
966 context_.args_tracker->Flush(); // Flush track args.
967
968 context_.storage->mutable_slice_table()->Insert(
969 {kTimestamp, kDuration, track, cat_id, name_id, 0, 0, 0});
970 StringId arg_key_id =
971 context_.storage->InternString(base::StringView(kArgName));
972 GlobalArgsTracker::Arg arg;
973 arg.flat_key = arg_key_id;
974 arg.key = arg_key_id;
975 arg.value = Variadic::Integer(kArgValue);
976 ArgSetId args = context_.global_args_tracker->AddArgSet({arg}, 0, 1);
977 context_.storage->mutable_slice_table()->mutable_arg_set_id()->Set(0, args);
978
979 // Child event with same timestamps as first one.
980 context_.storage->mutable_slice_table()->Insert(
981 {kTimestamp, kDuration, track, cat_id, name2_id, 0, 0, 0});
982
983 // Another overlapping async event on a different track.
984 context_.storage->mutable_slice_table()->Insert(
985 {kTimestamp3, kDuration3, track2, cat_id, name3_id, 0, 0, 0});
986
987 base::TempFile temp_file = base::TempFile::Create();
988 FILE* output = fopen(temp_file.path().c_str(), "w+");
989 util::Status status = ExportJson(context_.storage.get(), output);
990
991 EXPECT_TRUE(status.ok());
992
993 Json::Value result = ToJsonValue(ReadFile(output));
994 EXPECT_EQ(result["traceEvents"].size(), 6u);
995
996 // Events should be sorted by timestamp, with child slice's end before its
997 // parent.
998
999 Json::Value begin_event1 = result["traceEvents"][0];
1000 EXPECT_EQ(begin_event1["ph"].asString(), "b");
1001 EXPECT_EQ(begin_event1["ts"].asInt64(), kTimestamp / 1000);
1002 EXPECT_EQ(begin_event1["pid"].asInt(), static_cast<int>(kProcessID));
1003 EXPECT_EQ(begin_event1["id2"]["local"].asString(), "0xeb");
1004 EXPECT_EQ(begin_event1["cat"].asString(), kCategory);
1005 EXPECT_EQ(begin_event1["name"].asString(), kName);
1006 EXPECT_EQ(begin_event1["args"][kArgName].asInt(), kArgValue);
1007 EXPECT_FALSE(begin_event1.isMember("tts"));
1008 EXPECT_FALSE(begin_event1.isMember("use_async_tts"));
1009
1010 Json::Value begin_event2 = result["traceEvents"][1];
1011 EXPECT_EQ(begin_event2["ph"].asString(), "b");
1012 EXPECT_EQ(begin_event2["ts"].asInt64(), kTimestamp / 1000);
1013 EXPECT_EQ(begin_event2["pid"].asInt(), static_cast<int>(kProcessID));
1014 EXPECT_EQ(begin_event2["id2"]["local"].asString(), "0xeb");
1015 EXPECT_EQ(begin_event2["cat"].asString(), kCategory);
1016 EXPECT_EQ(begin_event2["name"].asString(), kName2);
1017 EXPECT_TRUE(begin_event2["args"].isObject());
1018 EXPECT_EQ(begin_event2["args"].size(), 0u);
1019 EXPECT_FALSE(begin_event2.isMember("tts"));
1020 EXPECT_FALSE(begin_event2.isMember("use_async_tts"));
1021
1022 Json::Value begin_event3 = result["traceEvents"][2];
1023 EXPECT_EQ(begin_event3["ph"].asString(), "b");
1024 EXPECT_EQ(begin_event3["ts"].asInt64(), kTimestamp3 / 1000);
1025 EXPECT_EQ(begin_event3["pid"].asInt(), static_cast<int>(kProcessID));
1026 EXPECT_EQ(begin_event3["id2"]["local"].asString(), "0xec");
1027 EXPECT_EQ(begin_event3["cat"].asString(), kCategory);
1028 EXPECT_EQ(begin_event3["name"].asString(), kName3);
1029 EXPECT_TRUE(begin_event3["args"].isObject());
1030 EXPECT_EQ(begin_event3["args"].size(), 0u);
1031 EXPECT_FALSE(begin_event3.isMember("tts"));
1032 EXPECT_FALSE(begin_event3.isMember("use_async_tts"));
1033
1034 Json::Value end_event2 = result["traceEvents"][3];
1035 EXPECT_EQ(end_event2["ph"].asString(), "e");
1036 EXPECT_EQ(end_event2["ts"].asInt64(), (kTimestamp + kDuration) / 1000);
1037 EXPECT_EQ(end_event2["pid"].asInt(), static_cast<int>(kProcessID));
1038 EXPECT_EQ(end_event2["id2"]["local"].asString(), "0xeb");
1039 EXPECT_EQ(end_event2["cat"].asString(), kCategory);
1040 EXPECT_EQ(end_event2["name"].asString(), kName2);
1041 EXPECT_TRUE(end_event2["args"].isObject());
1042 EXPECT_EQ(end_event2["args"].size(), 0u);
1043 EXPECT_FALSE(end_event2.isMember("tts"));
1044 EXPECT_FALSE(end_event2.isMember("use_async_tts"));
1045
1046 Json::Value end_event1 = result["traceEvents"][4];
1047 EXPECT_EQ(end_event1["ph"].asString(), "e");
1048 EXPECT_EQ(end_event1["ts"].asInt64(), (kTimestamp + kDuration) / 1000);
1049 EXPECT_EQ(end_event1["pid"].asInt(), static_cast<int>(kProcessID));
1050 EXPECT_EQ(end_event1["id2"]["local"].asString(), "0xeb");
1051 EXPECT_EQ(end_event1["cat"].asString(), kCategory);
1052 EXPECT_EQ(end_event1["name"].asString(), kName);
1053 EXPECT_TRUE(end_event1["args"].isObject());
1054 EXPECT_EQ(end_event1["args"].size(), 0u);
1055 EXPECT_FALSE(end_event1.isMember("tts"));
1056 EXPECT_FALSE(end_event1.isMember("use_async_tts"));
1057
1058 Json::Value end_event3 = result["traceEvents"][5];
1059 EXPECT_EQ(end_event3["ph"].asString(), "e");
1060 EXPECT_EQ(end_event3["ts"].asInt64(), (kTimestamp3 + kDuration3) / 1000);
1061 EXPECT_EQ(end_event3["pid"].asInt(), static_cast<int>(kProcessID));
1062 EXPECT_EQ(end_event3["id2"]["local"].asString(), "0xec");
1063 EXPECT_EQ(end_event3["cat"].asString(), kCategory);
1064 EXPECT_EQ(end_event3["name"].asString(), kName3);
1065 EXPECT_TRUE(end_event3["args"].isObject());
1066 EXPECT_EQ(end_event3["args"].size(), 0u);
1067 EXPECT_FALSE(end_event3.isMember("tts"));
1068 EXPECT_FALSE(end_event3.isMember("use_async_tts"));
1069 }
1070
TEST_F(ExportJsonTest,LegacyAsyncEvents)1071 TEST_F(ExportJsonTest, LegacyAsyncEvents) {
1072 using Arg = GlobalArgsTracker::Arg;
1073 const int64_t kTimestamp = 10000000;
1074 const int64_t kDuration = 100000;
1075 const int64_t kTimestamp2 = 10001000;
1076 const int64_t kDuration2 = 0;
1077 const int64_t kTimestamp3 = 10005000;
1078 const int64_t kDuration3 = 100000;
1079 const uint32_t kProcessID = 100;
1080 const char* kCategory = "cat";
1081 const char* kName = "name";
1082 const char* kName2 = "name2";
1083 const char* kName3 = "name3";
1084
1085 UniquePid upid = context_.process_tracker->GetOrCreateProcess(kProcessID);
1086 StringId cat_id = context_.storage->InternString(base::StringView(kCategory));
1087 StringId name_id = context_.storage->InternString(base::StringView(kName));
1088 StringId name2_id = context_.storage->InternString(base::StringView(kName2));
1089 StringId name3_id = context_.storage->InternString(base::StringView(kName3));
1090
1091 auto arg_inserter = [this](base::StringView arg_name,
1092 base::StringView arg_value,
1093 std::vector<Arg>& args) {
1094 Arg arg;
1095 StringId arg_key_id =
1096 context_.storage->InternString(base::StringView(arg_name));
1097 arg.flat_key = arg_key_id;
1098 arg.key = arg_key_id;
1099 StringId value_id = context_.storage->InternString(arg_value);
1100 arg.value = Variadic::String(value_id);
1101 args.push_back(arg);
1102 };
1103
1104 constexpr int64_t kSourceId = 235;
1105 TrackId track = context_.track_tracker->InternLegacyChromeAsyncTrack(
1106 name_id, upid, kSourceId, /*source_id_is_process_scoped=*/true,
1107 /*source_scope=*/kNullStringId);
1108 constexpr int64_t kSourceId2 = 236;
1109 TrackId track2 = context_.track_tracker->InternLegacyChromeAsyncTrack(
1110 name3_id, upid, kSourceId2, /*source_id_is_process_scoped=*/true,
1111 /*source_scope=*/kNullStringId);
1112 context_.args_tracker->Flush(); // Flush track args.
1113
1114 context_.storage->mutable_slice_table()->Insert(
1115 {kTimestamp, kDuration, track, cat_id, name_id, 0, 0, 0});
1116 std::vector<Arg> args1;
1117 arg_inserter("arg1", "value1", args1);
1118 arg_inserter("legacy_event.phase", "S", args1);
1119 ArgSetId arg_id1 = context_.global_args_tracker->AddArgSet(args1, 0, 2);
1120 context_.storage->mutable_slice_table()->mutable_arg_set_id()->Set(0,
1121 arg_id1);
1122
1123 // Step event with first event as parent.
1124 context_.storage->mutable_slice_table()->Insert(
1125 {kTimestamp2, kDuration2, track, cat_id, name2_id, 0, 0, 0});
1126 std::vector<Arg> step_args;
1127 arg_inserter("arg2", "value2", step_args);
1128 arg_inserter("legacy_event.phase", "T", step_args);
1129 arg_inserter("debug.step", "Step1", step_args);
1130 ArgSetId arg_id2 = context_.global_args_tracker->AddArgSet(step_args, 0, 3);
1131 context_.storage->mutable_slice_table()->mutable_arg_set_id()->Set(1,
1132 arg_id2);
1133
1134 // Another overlapping async event on a different track.
1135 context_.storage->mutable_slice_table()->Insert(
1136 {kTimestamp3, kDuration3, track2, cat_id, name3_id, 0, 0, 0});
1137 std::vector<Arg> args3;
1138 arg_inserter("legacy_event.phase", "S", args3);
1139 ArgSetId arg_id3 = context_.global_args_tracker->AddArgSet(args3, 0, 1);
1140 context_.storage->mutable_slice_table()->mutable_arg_set_id()->Set(2,
1141 arg_id3);
1142
1143 base::TempFile temp_file = base::TempFile::Create();
1144 FILE* output = fopen(temp_file.path().c_str(), "w+");
1145 util::Status status = ExportJson(context_.storage.get(), output);
1146
1147 EXPECT_TRUE(status.ok());
1148
1149 Json::Value result = ToJsonValue(ReadFile(output));
1150 EXPECT_EQ(result["traceEvents"].size(), 5u);
1151
1152 // Events should be sorted by timestamp, with child slice's end before its
1153 // parent.
1154
1155 Json::Value begin_event1 = result["traceEvents"][0];
1156 EXPECT_EQ(begin_event1["ph"].asString(), "S");
1157 EXPECT_EQ(begin_event1["ts"].asInt64(), kTimestamp / 1000);
1158 EXPECT_EQ(begin_event1["pid"].asInt(), static_cast<int>(kProcessID));
1159 EXPECT_EQ(begin_event1["id2"]["local"].asString(), "0xeb");
1160 EXPECT_EQ(begin_event1["cat"].asString(), kCategory);
1161 EXPECT_EQ(begin_event1["name"].asString(), kName);
1162 EXPECT_FALSE(begin_event1.isMember("tts"));
1163 EXPECT_FALSE(begin_event1.isMember("use_async_tts"));
1164 EXPECT_EQ(begin_event1["args"].size(), 1u);
1165 EXPECT_EQ(begin_event1["args"]["arg1"].asString(), "value1");
1166
1167 Json::Value step_event = result["traceEvents"][1];
1168 EXPECT_EQ(step_event["ph"].asString(), "T");
1169 EXPECT_EQ(step_event["ts"].asInt64(), kTimestamp2 / 1000);
1170 EXPECT_EQ(step_event["pid"].asInt(), static_cast<int>(kProcessID));
1171 EXPECT_EQ(step_event["id2"]["local"].asString(), "0xeb");
1172 EXPECT_EQ(step_event["cat"].asString(), kCategory);
1173 EXPECT_EQ(step_event["name"].asString(), kName2);
1174 EXPECT_TRUE(step_event["args"].isObject());
1175 EXPECT_EQ(step_event["args"].size(), 2u);
1176 EXPECT_EQ(step_event["args"]["arg2"].asString(), "value2");
1177 EXPECT_EQ(step_event["args"]["step"].asString(), "Step1");
1178
1179 Json::Value begin_event2 = result["traceEvents"][2];
1180 EXPECT_EQ(begin_event2["ph"].asString(), "S");
1181 EXPECT_EQ(begin_event2["ts"].asInt64(), kTimestamp3 / 1000);
1182 EXPECT_EQ(begin_event2["pid"].asInt(), static_cast<int>(kProcessID));
1183 EXPECT_EQ(begin_event2["id2"]["local"].asString(), "0xec");
1184 EXPECT_EQ(begin_event2["cat"].asString(), kCategory);
1185 EXPECT_EQ(begin_event2["name"].asString(), kName3);
1186 EXPECT_TRUE(begin_event2["args"].isObject());
1187 EXPECT_EQ(begin_event2["args"].size(), 0u);
1188 EXPECT_FALSE(begin_event2.isMember("tts"));
1189 EXPECT_FALSE(begin_event2.isMember("use_async_tts"));
1190
1191 Json::Value end_event1 = result["traceEvents"][3];
1192 EXPECT_EQ(end_event1["ph"].asString(), "F");
1193 EXPECT_EQ(end_event1["ts"].asInt64(), (kTimestamp + kDuration) / 1000);
1194 EXPECT_EQ(end_event1["pid"].asInt(), static_cast<int>(kProcessID));
1195 EXPECT_EQ(end_event1["id2"]["local"].asString(), "0xeb");
1196 EXPECT_EQ(end_event1["cat"].asString(), kCategory);
1197 EXPECT_EQ(end_event1["name"].asString(), kName);
1198 EXPECT_TRUE(end_event1["args"].isObject());
1199 EXPECT_EQ(end_event1["args"].size(), 0u);
1200 EXPECT_FALSE(end_event1.isMember("tts"));
1201 EXPECT_FALSE(end_event1.isMember("use_async_tts"));
1202
1203 Json::Value end_event3 = result["traceEvents"][4];
1204 EXPECT_EQ(end_event3["ph"].asString(), "F");
1205 EXPECT_EQ(end_event3["ts"].asInt64(), (kTimestamp3 + kDuration3) / 1000);
1206 EXPECT_EQ(end_event3["pid"].asInt(), static_cast<int>(kProcessID));
1207 EXPECT_EQ(end_event3["id2"]["local"].asString(), "0xec");
1208 EXPECT_EQ(end_event3["cat"].asString(), kCategory);
1209 EXPECT_EQ(end_event3["name"].asString(), kName3);
1210 EXPECT_TRUE(end_event3["args"].isObject());
1211 EXPECT_EQ(end_event3["args"].size(), 0u);
1212 EXPECT_FALSE(end_event3.isMember("tts"));
1213 EXPECT_FALSE(end_event3.isMember("use_async_tts"));
1214 }
1215
TEST_F(ExportJsonTest,AsyncEventWithThreadTimestamp)1216 TEST_F(ExportJsonTest, AsyncEventWithThreadTimestamp) {
1217 const int64_t kTimestamp = 10000000;
1218 const int64_t kDuration = 100000;
1219 const int64_t kThreadTimestamp = 10000001;
1220 const int64_t kThreadDuration = 99998;
1221 const uint32_t kProcessID = 100;
1222 const char* kCategory = "cat";
1223 const char* kName = "name";
1224
1225 UniquePid upid = context_.process_tracker->GetOrCreateProcess(kProcessID);
1226 StringId cat_id = context_.storage->InternString(base::StringView(kCategory));
1227 StringId name_id = context_.storage->InternString(base::StringView(kName));
1228
1229 constexpr int64_t kSourceId = 235;
1230 TrackId track = context_.track_tracker->InternLegacyChromeAsyncTrack(
1231 name_id, upid, kSourceId, /*source_id_is_process_scoped=*/true,
1232 /*source_scope=*/kNullStringId);
1233 context_.args_tracker->Flush(); // Flush track args.
1234
1235 auto* slices = context_.storage->mutable_slice_table();
1236 auto id_and_row =
1237 slices->Insert({kTimestamp, kDuration, track, cat_id, name_id, 0, 0, 0});
1238 context_.storage->mutable_virtual_track_slices()->AddVirtualTrackSlice(
1239 id_and_row.id, kThreadTimestamp, kThreadDuration, 0, 0);
1240
1241 base::TempFile temp_file = base::TempFile::Create();
1242 FILE* output = fopen(temp_file.path().c_str(), "w+");
1243 util::Status status = ExportJson(context_.storage.get(), output);
1244
1245 EXPECT_TRUE(status.ok());
1246
1247 Json::Value result = ToJsonValue(ReadFile(output));
1248 EXPECT_EQ(result["traceEvents"].size(), 2u);
1249
1250 Json::Value begin_event = result["traceEvents"][0];
1251 EXPECT_EQ(begin_event["ph"].asString(), "b");
1252 EXPECT_EQ(begin_event["ts"].asInt64(), kTimestamp / 1000);
1253 EXPECT_EQ(begin_event["tts"].asInt64(), kThreadTimestamp / 1000);
1254 EXPECT_EQ(begin_event["use_async_tts"].asInt(), 1);
1255 EXPECT_EQ(begin_event["pid"].asInt(), static_cast<int>(kProcessID));
1256 EXPECT_EQ(begin_event["id2"]["local"].asString(), "0xeb");
1257 EXPECT_EQ(begin_event["cat"].asString(), kCategory);
1258 EXPECT_EQ(begin_event["name"].asString(), kName);
1259
1260 Json::Value end_event = result["traceEvents"][1];
1261 EXPECT_EQ(end_event["ph"].asString(), "e");
1262 EXPECT_EQ(end_event["ts"].asInt64(), (kTimestamp + kDuration) / 1000);
1263 EXPECT_EQ(end_event["tts"].asInt64(),
1264 (kThreadTimestamp + kThreadDuration) / 1000);
1265 EXPECT_EQ(end_event["use_async_tts"].asInt(), 1);
1266 EXPECT_EQ(end_event["pid"].asInt(), static_cast<int>(kProcessID));
1267 EXPECT_EQ(end_event["id2"]["local"].asString(), "0xeb");
1268 EXPECT_EQ(end_event["cat"].asString(), kCategory);
1269 EXPECT_EQ(end_event["name"].asString(), kName);
1270 }
1271
TEST_F(ExportJsonTest,UnfinishedAsyncEvent)1272 TEST_F(ExportJsonTest, UnfinishedAsyncEvent) {
1273 const int64_t kTimestamp = 10000000;
1274 const int64_t kDuration = -1;
1275 const int64_t kThreadTimestamp = 10000001;
1276 const int64_t kThreadDuration = -1;
1277 const uint32_t kProcessID = 100;
1278 const char* kCategory = "cat";
1279 const char* kName = "name";
1280
1281 UniquePid upid = context_.process_tracker->GetOrCreateProcess(kProcessID);
1282 StringId cat_id = context_.storage->InternString(base::StringView(kCategory));
1283 StringId name_id = context_.storage->InternString(base::StringView(kName));
1284
1285 constexpr int64_t kSourceId = 235;
1286 TrackId track = context_.track_tracker->InternLegacyChromeAsyncTrack(
1287 name_id, upid, kSourceId, /*source_id_is_process_scoped=*/true,
1288 /*source_scope=*/kNullStringId);
1289 context_.args_tracker->Flush(); // Flush track args.
1290
1291 auto slice_id =
1292 context_.storage->mutable_slice_table()
1293 ->Insert({kTimestamp, kDuration, track, cat_id, name_id, 0, 0, 0})
1294 .id;
1295 context_.storage->mutable_virtual_track_slices()->AddVirtualTrackSlice(
1296 slice_id, kThreadTimestamp, kThreadDuration, 0, 0);
1297
1298 base::TempFile temp_file = base::TempFile::Create();
1299 FILE* output = fopen(temp_file.path().c_str(), "w+");
1300 util::Status status = ExportJson(context_.storage.get(), output);
1301
1302 EXPECT_TRUE(status.ok());
1303
1304 Json::Value result = ToJsonValue(ReadFile(output));
1305 EXPECT_EQ(result["traceEvents"].size(), 1u);
1306
1307 Json::Value begin_event = result["traceEvents"][0];
1308 EXPECT_EQ(begin_event["ph"].asString(), "b");
1309 EXPECT_EQ(begin_event["ts"].asInt64(), kTimestamp / 1000);
1310 EXPECT_EQ(begin_event["tts"].asInt64(), kThreadTimestamp / 1000);
1311 EXPECT_EQ(begin_event["use_async_tts"].asInt(), 1);
1312 EXPECT_EQ(begin_event["pid"].asInt(), static_cast<int>(kProcessID));
1313 EXPECT_EQ(begin_event["id2"]["local"].asString(), "0xeb");
1314 EXPECT_EQ(begin_event["cat"].asString(), kCategory);
1315 EXPECT_EQ(begin_event["name"].asString(), kName);
1316 }
1317
TEST_F(ExportJsonTest,AsyncInstantEvent)1318 TEST_F(ExportJsonTest, AsyncInstantEvent) {
1319 const int64_t kTimestamp = 10000000;
1320 const uint32_t kProcessID = 100;
1321 const char* kCategory = "cat";
1322 const char* kName = "name";
1323 const char* kArgName = "arg_name";
1324 const int kArgValue = 123;
1325
1326 UniquePid upid = context_.process_tracker->GetOrCreateProcess(kProcessID);
1327 StringId cat_id = context_.storage->InternString(base::StringView(kCategory));
1328 StringId name_id = context_.storage->InternString(base::StringView(kName));
1329
1330 constexpr int64_t kSourceId = 235;
1331 TrackId track = context_.track_tracker->InternLegacyChromeAsyncTrack(
1332 name_id, upid, kSourceId, /*source_id_is_process_scoped=*/true,
1333 /*source_scope=*/kNullStringId);
1334 context_.args_tracker->Flush(); // Flush track args.
1335
1336 context_.storage->mutable_slice_table()->Insert(
1337 {kTimestamp, 0, track, cat_id, name_id, 0, 0, 0});
1338 StringId arg_key_id =
1339 context_.storage->InternString(base::StringView("arg_name"));
1340 GlobalArgsTracker::Arg arg;
1341 arg.flat_key = arg_key_id;
1342 arg.key = arg_key_id;
1343 arg.value = Variadic::Integer(kArgValue);
1344 ArgSetId args = context_.global_args_tracker->AddArgSet({arg}, 0, 1);
1345 context_.storage->mutable_slice_table()->mutable_arg_set_id()->Set(0, args);
1346
1347 base::TempFile temp_file = base::TempFile::Create();
1348 FILE* output = fopen(temp_file.path().c_str(), "w+");
1349 util::Status status = ExportJson(context_.storage.get(), output);
1350
1351 EXPECT_TRUE(status.ok());
1352
1353 Json::Value result = ToJsonValue(ReadFile(output));
1354 EXPECT_EQ(result["traceEvents"].size(), 1u);
1355
1356 Json::Value event = result["traceEvents"][0];
1357 EXPECT_EQ(event["ph"].asString(), "n");
1358 EXPECT_EQ(event["ts"].asInt64(), kTimestamp / 1000);
1359 EXPECT_EQ(event["pid"].asInt(), static_cast<int>(kProcessID));
1360 EXPECT_EQ(event["id2"]["local"].asString(), "0xeb");
1361 EXPECT_EQ(event["cat"].asString(), kCategory);
1362 EXPECT_EQ(event["name"].asString(), kName);
1363 EXPECT_EQ(event["args"][kArgName].asInt(), kArgValue);
1364 }
1365
TEST_F(ExportJsonTest,RawEvent)1366 TEST_F(ExportJsonTest, RawEvent) {
1367 const int64_t kTimestamp = 10000000;
1368 const int64_t kDuration = 10000;
1369 const int64_t kThreadTimestamp = 20000000;
1370 const int64_t kThreadDuration = 20000;
1371 const int64_t kThreadInstructionCount = 30000000;
1372 const int64_t kThreadInstructionDelta = 30000;
1373 const uint32_t kProcessID = 100;
1374 const uint32_t kThreadID = 200;
1375 const char* kCategory = "cat";
1376 const char* kName = "name";
1377 const char* kPhase = "?";
1378 const uint64_t kGlobalId = 0xaaffaaffaaffaaff;
1379 const char* kIdScope = "my_id";
1380 const uint64_t kBindId = 0xaa00aa00aa00aa00;
1381 const char* kFlowDirection = "inout";
1382 const char* kArgName = "arg_name";
1383 const int kArgValue = 123;
1384
1385 TraceStorage* storage = context_.storage.get();
1386
1387 UniqueTid utid = context_.process_tracker->GetOrCreateThread(kThreadID);
1388 UniquePid upid = context_.process_tracker->GetOrCreateProcess(kProcessID);
1389 context_.storage->mutable_thread_table()->mutable_upid()->Set(utid, upid);
1390
1391 auto id_and_row = storage->mutable_raw_table()->Insert(
1392 {kTimestamp, storage->InternString("track_event.legacy_event"), /*cpu=*/0,
1393 utid});
1394 auto inserter = context_.args_tracker->AddArgsTo(id_and_row.id);
1395
1396 auto add_arg = [&](const char* key, Variadic value) {
1397 StringId key_id = storage->InternString(key);
1398 inserter.AddArg(key_id, value);
1399 };
1400
1401 StringId cat_id = storage->InternString(base::StringView(kCategory));
1402 add_arg("legacy_event.category", Variadic::String(cat_id));
1403 StringId name_id = storage->InternString(base::StringView(kName));
1404 add_arg("legacy_event.name", Variadic::String(name_id));
1405 StringId phase_id = storage->InternString(base::StringView(kPhase));
1406 add_arg("legacy_event.phase", Variadic::String(phase_id));
1407
1408 add_arg("legacy_event.duration_ns", Variadic::Integer(kDuration));
1409 add_arg("legacy_event.thread_timestamp_ns",
1410 Variadic::Integer(kThreadTimestamp));
1411 add_arg("legacy_event.thread_duration_ns",
1412 Variadic::Integer(kThreadDuration));
1413 add_arg("legacy_event.thread_instruction_count",
1414 Variadic::Integer(kThreadInstructionCount));
1415 add_arg("legacy_event.thread_instruction_delta",
1416 Variadic::Integer(kThreadInstructionDelta));
1417 add_arg("legacy_event.use_async_tts", Variadic::Boolean(true));
1418 add_arg("legacy_event.global_id", Variadic::UnsignedInteger(kGlobalId));
1419 StringId scope_id = storage->InternString(base::StringView(kIdScope));
1420 add_arg("legacy_event.id_scope", Variadic::String(scope_id));
1421 add_arg("legacy_event.bind_id", Variadic::UnsignedInteger(kBindId));
1422 add_arg("legacy_event.bind_to_enclosing", Variadic::Boolean(true));
1423 StringId flow_direction_id = storage->InternString(kFlowDirection);
1424 add_arg("legacy_event.flow_direction", Variadic::String(flow_direction_id));
1425
1426 add_arg(kArgName, Variadic::Integer(kArgValue));
1427
1428 context_.args_tracker->Flush();
1429
1430 base::TempFile temp_file = base::TempFile::Create();
1431 FILE* output = fopen(temp_file.path().c_str(), "w+");
1432 util::Status status = ExportJson(storage, output);
1433
1434 EXPECT_TRUE(status.ok());
1435
1436 Json::Value result = ToJsonValue(ReadFile(output));
1437 EXPECT_EQ(result["traceEvents"].size(), 1u);
1438
1439 Json::Value event = result["traceEvents"][0];
1440 EXPECT_EQ(event["ph"].asString(), kPhase);
1441 EXPECT_EQ(event["ts"].asInt64(), kTimestamp / 1000);
1442 EXPECT_EQ(event["dur"].asInt64(), kDuration / 1000);
1443 EXPECT_EQ(event["tts"].asInt64(), kThreadTimestamp / 1000);
1444 EXPECT_EQ(event["tdur"].asInt64(), kThreadDuration / 1000);
1445 EXPECT_EQ(event["ticount"].asInt64(), kThreadInstructionCount);
1446 EXPECT_EQ(event["tidelta"].asInt64(), kThreadInstructionDelta);
1447 EXPECT_EQ(event["tid"].asInt(), static_cast<int>(kThreadID));
1448 EXPECT_EQ(event["cat"].asString(), kCategory);
1449 EXPECT_EQ(event["name"].asString(), kName);
1450 EXPECT_EQ(event["use_async_tts"].asInt(), 1);
1451 EXPECT_EQ(event["id2"]["global"].asString(), "0xaaffaaffaaffaaff");
1452 EXPECT_EQ(event["scope"].asString(), kIdScope);
1453 EXPECT_EQ(event["args"][kArgName].asInt(), kArgValue);
1454 }
1455
TEST_F(ExportJsonTest,LegacyRawEvents)1456 TEST_F(ExportJsonTest, LegacyRawEvents) {
1457 const char* kLegacyFtraceData = "some \"data\"\nsome :data:";
1458 const char* kLegacyJsonData1 = "{\"us";
1459 const char* kLegacyJsonData2 = "er\": 1},{\"user\": 2}";
1460
1461 TraceStorage* storage = context_.storage.get();
1462 auto* raw = storage->mutable_raw_table();
1463
1464 auto id_and_row = raw->Insert(
1465 {0, storage->InternString("chrome_event.legacy_system_trace"), 0, 0});
1466 auto inserter = context_.args_tracker->AddArgsTo(id_and_row.id);
1467
1468 StringId data_id = storage->InternString("data");
1469 StringId ftrace_data_id = storage->InternString(kLegacyFtraceData);
1470 inserter.AddArg(data_id, Variadic::String(ftrace_data_id));
1471
1472 id_and_row = raw->Insert(
1473 {0, storage->InternString("chrome_event.legacy_user_trace"), 0, 0});
1474 inserter = context_.args_tracker->AddArgsTo(id_and_row.id);
1475 StringId json_data1_id = storage->InternString(kLegacyJsonData1);
1476 inserter.AddArg(data_id, Variadic::String(json_data1_id));
1477
1478 id_and_row = raw->Insert(
1479 {0, storage->InternString("chrome_event.legacy_user_trace"), 0, 0});
1480 inserter = context_.args_tracker->AddArgsTo(id_and_row.id);
1481 StringId json_data2_id = storage->InternString(kLegacyJsonData2);
1482 inserter.AddArg(data_id, Variadic::String(json_data2_id));
1483
1484 context_.args_tracker->Flush();
1485
1486 base::TempFile temp_file = base::TempFile::Create();
1487 FILE* output = fopen(temp_file.path().c_str(), "w+");
1488 util::Status status = ExportJson(storage, output);
1489
1490 EXPECT_TRUE(status.ok());
1491
1492 Json::Value result = ToJsonValue(ReadFile(output));
1493
1494 EXPECT_EQ(result["traceEvents"].size(), 2u);
1495 EXPECT_EQ(result["traceEvents"][0]["user"].asInt(), 1);
1496 EXPECT_EQ(result["traceEvents"][1]["user"].asInt(), 2);
1497 EXPECT_EQ(result["systemTraceEvents"].asString(), kLegacyFtraceData);
1498 }
1499
TEST_F(ExportJsonTest,CpuProfileEvent)1500 TEST_F(ExportJsonTest, CpuProfileEvent) {
1501 const uint32_t kProcessID = 100;
1502 const uint32_t kThreadID = 200;
1503 const int64_t kTimestamp = 10000000;
1504 const int32_t kProcessPriority = 42;
1505
1506 TraceStorage* storage = context_.storage.get();
1507
1508 UniqueTid utid = context_.process_tracker->GetOrCreateThread(kThreadID);
1509 UniquePid upid = context_.process_tracker->GetOrCreateProcess(kProcessID);
1510 context_.storage->mutable_thread_table()->mutable_upid()->Set(utid, upid);
1511
1512 auto* mappings = storage->mutable_stack_profile_mapping_table();
1513 auto* frames = storage->mutable_stack_profile_frame_table();
1514 auto* callsites = storage->mutable_stack_profile_callsite_table();
1515
1516 auto module_1 =
1517 mappings->Insert({storage->InternString("foo_module_id"), 0, 0, 0, 0, 0,
1518 storage->InternString("foo_module_name")});
1519
1520 auto module_2 =
1521 mappings->Insert({storage->InternString("bar_module_id"), 0, 0, 0, 0, 0,
1522 storage->InternString("bar_module_name")});
1523
1524 // TODO(140860736): Once we support null values for
1525 // stack_profile_frame.symbol_set_id remove this hack
1526 storage->mutable_symbol_table()->Insert({0, kNullStringId, kNullStringId, 0});
1527
1528 auto frame_1 = frames->Insert({/*name_id=*/kNullStringId, module_1.id, 0x42});
1529
1530 uint32_t symbol_set_id = storage->symbol_table().row_count();
1531 storage->mutable_symbol_table()->Insert(
1532 {symbol_set_id, storage->InternString("foo_func"),
1533 storage->InternString("foo_file"), 66});
1534 frames->mutable_symbol_set_id()->Set(frame_1.row, symbol_set_id);
1535
1536 auto frame_2 =
1537 frames->Insert({/*name_id=*/kNullStringId, module_2.id, 0x4242});
1538
1539 symbol_set_id = storage->symbol_table().row_count();
1540 storage->mutable_symbol_table()->Insert(
1541 {symbol_set_id, storage->InternString("bar_func"),
1542 storage->InternString("bar_file"), 77});
1543 frames->mutable_symbol_set_id()->Set(frame_2.row, symbol_set_id);
1544
1545 auto frame_callsite_1 = callsites->Insert({0, std::nullopt, frame_1.id});
1546
1547 auto frame_callsite_2 =
1548 callsites->Insert({1, frame_callsite_1.id, frame_2.id});
1549
1550 storage->mutable_cpu_profile_stack_sample_table()->Insert(
1551 {kTimestamp, frame_callsite_2.id, utid, kProcessPriority});
1552
1553 storage->mutable_cpu_profile_stack_sample_table()->Insert(
1554 {kTimestamp + 10000, frame_callsite_1.id, utid, kProcessPriority});
1555
1556 storage->mutable_cpu_profile_stack_sample_table()->Insert(
1557 {kTimestamp + 20000, frame_callsite_1.id, utid, kProcessPriority});
1558
1559 base::TempFile temp_file = base::TempFile::Create();
1560 FILE* output = fopen(temp_file.path().c_str(), "w+");
1561 util::Status status = ExportJson(storage, output);
1562
1563 EXPECT_TRUE(status.ok());
1564
1565 Json::Value result = ToJsonValue(ReadFile(output));
1566
1567 // The first sample should generate only a single instant event;
1568 // the two following samples should also generate an additional [b, e] pair
1569 // (the async duration event).
1570 EXPECT_EQ(result["traceEvents"].size(), 5u);
1571 Json::Value event = result["traceEvents"][0];
1572 EXPECT_EQ(event["ph"].asString(), "n");
1573 EXPECT_EQ(event["id"].asString(), "0x1");
1574 EXPECT_EQ(event["ts"].asInt64(), kTimestamp / 1000);
1575 EXPECT_EQ(event["tid"].asInt(), static_cast<int>(kThreadID));
1576 EXPECT_EQ(event["cat"].asString(), "disabled-by-default-cpu_profiler");
1577 EXPECT_EQ(event["name"].asString(), "StackCpuSampling");
1578 EXPECT_EQ(event["s"].asString(), "t");
1579 EXPECT_EQ(event["args"]["frames"].asString(),
1580 "foo_func - foo_module_name [foo_module_id]\nbar_func - "
1581 "bar_module_name [bar_module_id]\n");
1582 EXPECT_EQ(event["args"]["process_priority"].asInt(), kProcessPriority);
1583
1584 event = result["traceEvents"][1];
1585 EXPECT_EQ(event["ph"].asString(), "n");
1586 EXPECT_EQ(event["id"].asString(), "0x2");
1587 EXPECT_EQ(event["ts"].asInt64(), (kTimestamp + 10000) / 1000);
1588
1589 event = result["traceEvents"][2];
1590 EXPECT_EQ(event["ph"].asString(), "n");
1591 EXPECT_EQ(event["id"].asString(), "0x2");
1592 EXPECT_EQ(event["ts"].asInt64(), (kTimestamp + 20000) / 1000);
1593 Json::String second_callstack_ = event["args"]["frames"].asString();
1594 EXPECT_EQ(second_callstack_, "foo_func - foo_module_name [foo_module_id]\n");
1595
1596 event = result["traceEvents"][3];
1597 EXPECT_EQ(event["ph"].asString(), "b");
1598 EXPECT_EQ(event["id"].asString(), "0x2");
1599 EXPECT_EQ(event["ts"].asInt64(), (kTimestamp + 10000) / 1000 - 1);
1600 EXPECT_EQ(event["args"]["frames"].asString(), second_callstack_);
1601
1602 event = result["traceEvents"][4];
1603 EXPECT_EQ(event["ph"].asString(), "e");
1604 EXPECT_EQ(event["id"].asString(), "0x2");
1605 EXPECT_EQ(event["ts"].asInt64(), (kTimestamp + 20000) / 1000);
1606 }
1607
TEST_F(ExportJsonTest,ArgumentFilter)1608 TEST_F(ExportJsonTest, ArgumentFilter) {
1609 UniqueTid utid = context_.process_tracker->GetOrCreateThread(0);
1610 TrackId track = context_.track_tracker->InternThreadTrack(utid);
1611 context_.args_tracker->Flush(); // Flush track args.
1612
1613 StringId cat_id = context_.storage->InternString(base::StringView("cat"));
1614 std::array<StringId, 3> name_ids{
1615 context_.storage->InternString(base::StringView("name1")),
1616 context_.storage->InternString(base::StringView("name2")),
1617 context_.storage->InternString(base::StringView("name3"))};
1618 StringId arg1_id = context_.storage->InternString(base::StringView("arg1"));
1619 StringId arg2_id = context_.storage->InternString(base::StringView("arg2"));
1620 StringId val_id = context_.storage->InternString(base::StringView("val"));
1621
1622 auto* slices = context_.storage->mutable_slice_table();
1623 std::vector<ArgsTracker::BoundInserter> slice_inserters;
1624 for (size_t i = 0; i < name_ids.size(); i++) {
1625 auto id = slices->Insert({0, 0, track, cat_id, name_ids[i], 0, 0, 0}).id;
1626 slice_inserters.emplace_back(context_.args_tracker->AddArgsTo(id));
1627 }
1628
1629 for (auto& inserter : slice_inserters) {
1630 inserter.AddArg(arg1_id, Variadic::Integer(5))
1631 .AddArg(arg2_id, Variadic::String(val_id));
1632 }
1633 context_.args_tracker->Flush();
1634
1635 auto arg_filter = [](const char* category_group_name, const char* event_name,
1636 ArgumentNameFilterPredicate* arg_name_filter) {
1637 EXPECT_TRUE(strcmp(category_group_name, "cat") == 0);
1638 if (strcmp(event_name, "name1") == 0) {
1639 // Filter all args for name1.
1640 return false;
1641 } else if (strcmp(event_name, "name2") == 0) {
1642 // Filter only the second arg for name2.
1643 *arg_name_filter = [](const char* arg_name) {
1644 if (strcmp(arg_name, "arg1") == 0) {
1645 return true;
1646 }
1647 EXPECT_TRUE(strcmp(arg_name, "arg2") == 0);
1648 return false;
1649 };
1650 return true;
1651 }
1652 // Filter no args for name3.
1653 EXPECT_TRUE(strcmp(event_name, "name3") == 0);
1654 return true;
1655 };
1656
1657 Json::Value result = ToJsonValue(ToJson(arg_filter));
1658
1659 EXPECT_EQ(result["traceEvents"].size(), 3u);
1660
1661 EXPECT_EQ(result["traceEvents"][0]["cat"].asString(), "cat");
1662 EXPECT_EQ(result["traceEvents"][0]["name"].asString(), "name1");
1663 EXPECT_EQ(result["traceEvents"][0]["args"].asString(), "__stripped__");
1664
1665 EXPECT_EQ(result["traceEvents"][1]["cat"].asString(), "cat");
1666 EXPECT_EQ(result["traceEvents"][1]["name"].asString(), "name2");
1667 EXPECT_EQ(result["traceEvents"][1]["args"]["arg1"].asInt(), 5);
1668 EXPECT_EQ(result["traceEvents"][1]["args"]["arg2"].asString(),
1669 "__stripped__");
1670
1671 EXPECT_EQ(result["traceEvents"][2]["cat"].asString(), "cat");
1672 EXPECT_EQ(result["traceEvents"][2]["name"].asString(), "name3");
1673 EXPECT_EQ(result["traceEvents"][2]["args"]["arg1"].asInt(), 5);
1674 EXPECT_EQ(result["traceEvents"][2]["args"]["arg2"].asString(), "val");
1675 }
1676
TEST_F(ExportJsonTest,MetadataFilter)1677 TEST_F(ExportJsonTest, MetadataFilter) {
1678 const char* kName1 = "name1";
1679 const char* kName2 = "name2";
1680 const char* kValue1 = "value1";
1681 const int kValue2 = 222;
1682
1683 TraceStorage* storage = context_.storage.get();
1684
1685 auto* raw = storage->mutable_raw_table();
1686 RawId id =
1687 raw->Insert({0, storage->InternString("chrome_event.metadata"), 0, 0}).id;
1688
1689 StringId name1_id = storage->InternString(base::StringView(kName1));
1690 StringId name2_id = storage->InternString(base::StringView(kName2));
1691 StringId value1_id = storage->InternString(base::StringView(kValue1));
1692
1693 context_.args_tracker->AddArgsTo(id)
1694 .AddArg(name1_id, Variadic::String(value1_id))
1695 .AddArg(name2_id, Variadic::Integer(kValue2));
1696 context_.args_tracker->Flush();
1697
1698 auto metadata_filter = [](const char* metadata_name) {
1699 // Only allow name1.
1700 return strcmp(metadata_name, "name1") == 0;
1701 };
1702
1703 Json::Value result = ToJsonValue(ToJson(nullptr, metadata_filter));
1704
1705 EXPECT_TRUE(result.isMember("metadata"));
1706 Json::Value metadata = result["metadata"];
1707
1708 EXPECT_EQ(metadata[kName1].asString(), kValue1);
1709 EXPECT_EQ(metadata[kName2].asString(), "__stripped__");
1710 }
1711
TEST_F(ExportJsonTest,LabelFilter)1712 TEST_F(ExportJsonTest, LabelFilter) {
1713 const int64_t kTimestamp1 = 10000000;
1714 const int64_t kTimestamp2 = 20000000;
1715 const int64_t kDuration = 10000;
1716 const uint32_t kThreadID = 100;
1717 const char* kCategory = "cat";
1718 const char* kName = "name";
1719
1720 UniqueTid utid = context_.process_tracker->GetOrCreateThread(kThreadID);
1721 TrackId track = context_.track_tracker->InternThreadTrack(utid);
1722 context_.args_tracker->Flush(); // Flush track args.
1723 StringId cat_id = context_.storage->InternString(base::StringView(kCategory));
1724 StringId name_id = context_.storage->InternString(base::StringView(kName));
1725
1726 context_.storage->mutable_slice_table()->Insert(
1727 {kTimestamp1, kDuration, track, cat_id, name_id, 0, 0, 0});
1728 context_.storage->mutable_slice_table()->Insert(
1729 {kTimestamp2, kDuration, track, cat_id, name_id, 0, 0, 0});
1730
1731 auto label_filter = [](const char* label_name) {
1732 return strcmp(label_name, "traceEvents") == 0;
1733 };
1734
1735 Json::Value result =
1736 ToJsonValue("[" + ToJson(nullptr, nullptr, label_filter) + "]");
1737
1738 EXPECT_TRUE(result.isArray());
1739 EXPECT_EQ(result.size(), 2u);
1740
1741 EXPECT_EQ(result[0]["ph"].asString(), "X");
1742 EXPECT_EQ(result[0]["ts"].asInt64(), kTimestamp1 / 1000);
1743 EXPECT_EQ(result[0]["dur"].asInt64(), kDuration / 1000);
1744 EXPECT_EQ(result[0]["tid"].asInt(), static_cast<int>(kThreadID));
1745 EXPECT_EQ(result[0]["cat"].asString(), kCategory);
1746 EXPECT_EQ(result[0]["name"].asString(), kName);
1747 EXPECT_EQ(result[1]["ph"].asString(), "X");
1748 EXPECT_EQ(result[1]["ts"].asInt64(), kTimestamp2 / 1000);
1749 EXPECT_EQ(result[1]["dur"].asInt64(), kDuration / 1000);
1750 EXPECT_EQ(result[1]["tid"].asInt(), static_cast<int>(kThreadID));
1751 EXPECT_EQ(result[1]["cat"].asString(), kCategory);
1752 EXPECT_EQ(result[1]["name"].asString(), kName);
1753 }
1754
TEST_F(ExportJsonTest,MemorySnapshotOsDumpEvent)1755 TEST_F(ExportJsonTest, MemorySnapshotOsDumpEvent) {
1756 const int64_t kTimestamp = 10000000;
1757 const int64_t kPeakResidentSetSize = 100000;
1758 const int64_t kPrivateFootprintBytes = 200000;
1759 const int64_t kProtectionFlags = 1;
1760 const int64_t kStartAddress = 1000000000;
1761 const int64_t kSizeKb = 1000;
1762 const int64_t kPrivateCleanResidentKb = 2000;
1763 const int64_t kPrivateDirtyKb = 3000;
1764 const int64_t kProportionalResidentKb = 4000;
1765 const int64_t kSharedCleanResidentKb = 5000;
1766 const int64_t kSharedDirtyResidentKb = 6000;
1767 const int64_t kSwapKb = 7000;
1768 const int64_t kModuleTimestamp = 20000000;
1769 const uint32_t kProcessID = 100;
1770 const bool kIsPeakRssResettable = true;
1771 const char* kLevelOfDetail = "detailed";
1772 const char* kFileName = "filename";
1773 const char* kModuleDebugid = "debugid";
1774 const char* kModuleDebugPath = "debugpath";
1775
1776 UniquePid upid = context_.process_tracker->GetOrCreateProcess(kProcessID);
1777 TrackId track = context_.track_tracker->InternProcessTrack(upid);
1778 StringId level_of_detail_id =
1779 context_.storage->InternString(base::StringView(kLevelOfDetail));
1780 auto snapshot_id = context_.storage->mutable_memory_snapshot_table()
1781 ->Insert({kTimestamp, track, level_of_detail_id})
1782 .id;
1783
1784 StringId peak_resident_set_size_id =
1785 context_.storage->InternString("chrome.peak_resident_set_kb");
1786 TrackId peak_resident_set_size_counter =
1787 context_.track_tracker->InternProcessCounterTrack(
1788 peak_resident_set_size_id, upid);
1789 context_.event_tracker->PushCounter(kTimestamp, kPeakResidentSetSize,
1790 peak_resident_set_size_counter);
1791
1792 StringId private_footprint_bytes_id =
1793 context_.storage->InternString("chrome.private_footprint_kb");
1794 TrackId private_footprint_bytes_counter =
1795 context_.track_tracker->InternProcessCounterTrack(
1796 private_footprint_bytes_id, upid);
1797 context_.event_tracker->PushCounter(kTimestamp, kPrivateFootprintBytes,
1798 private_footprint_bytes_counter);
1799
1800 StringId is_peak_rss_resettable_id =
1801 context_.storage->InternString("is_peak_rss_resettable");
1802 context_.args_tracker->AddArgsTo(upid).AddArg(
1803 is_peak_rss_resettable_id, Variadic::Boolean(kIsPeakRssResettable));
1804 context_.args_tracker->Flush();
1805
1806 context_.storage->mutable_profiler_smaps_table()->Insert(
1807 {upid, kTimestamp, kNullStringId, kSizeKb, kPrivateDirtyKb, kSwapKb,
1808 context_.storage->InternString(kFileName), kStartAddress,
1809 kModuleTimestamp, context_.storage->InternString(kModuleDebugid),
1810 context_.storage->InternString(kModuleDebugPath), kProtectionFlags,
1811 kPrivateCleanResidentKb, kSharedDirtyResidentKb, kSharedCleanResidentKb,
1812 0, kProportionalResidentKb});
1813
1814 base::TempFile temp_file = base::TempFile::Create();
1815 FILE* output = fopen(temp_file.path().c_str(), "w+");
1816 util::Status status = ExportJson(context_.storage.get(), output);
1817
1818 EXPECT_TRUE(status.ok());
1819
1820 Json::Value result = ToJsonValue(ReadFile(output));
1821 EXPECT_EQ(result["traceEvents"].size(), 1u);
1822
1823 Json::Value event = result["traceEvents"][0];
1824 EXPECT_EQ(event["ph"].asString(), "v");
1825 EXPECT_EQ(event["cat"].asString(), "disabled-by-default-memory-infra");
1826 EXPECT_EQ(event["id"].asString(), base::Uint64ToHexString(snapshot_id.value));
1827 EXPECT_EQ(event["ts"].asInt64(), kTimestamp / 1000);
1828 EXPECT_EQ(event["name"].asString(), "periodic_interval");
1829 EXPECT_EQ(event["pid"].asUInt(), kProcessID);
1830 EXPECT_EQ(event["tid"].asInt(), -1);
1831
1832 EXPECT_TRUE(event["args"].isObject());
1833 EXPECT_EQ(event["args"]["dumps"]["level_of_detail"].asString(),
1834 kLevelOfDetail);
1835
1836 EXPECT_EQ(event["args"]["dumps"]["process_totals"]["peak_resident_set_size"]
1837 .asString(),
1838 base::Uint64ToHexStringNoPrefix(
1839 static_cast<uint64_t>(kPeakResidentSetSize)));
1840 EXPECT_EQ(event["args"]["dumps"]["process_totals"]["private_footprint_bytes"]
1841 .asString(),
1842 base::Uint64ToHexStringNoPrefix(
1843 static_cast<uint64_t>(kPrivateFootprintBytes)));
1844 EXPECT_EQ(event["args"]["dumps"]["process_totals"]["is_peak_rss_resettable"]
1845 .asBool(),
1846 kIsPeakRssResettable);
1847
1848 EXPECT_TRUE(event["args"]["dumps"]["process_mmaps"]["vm_regions"].isArray());
1849 EXPECT_EQ(event["args"]["dumps"]["process_mmaps"]["vm_regions"].size(), 1u);
1850 Json::Value region = event["args"]["dumps"]["process_mmaps"]["vm_regions"][0];
1851 EXPECT_EQ(region["mf"].asString(), kFileName);
1852 EXPECT_EQ(region["pf"].asInt64(), kProtectionFlags);
1853 EXPECT_EQ(region["sa"].asString(), base::Uint64ToHexStringNoPrefix(
1854 static_cast<uint64_t>(kStartAddress)));
1855 EXPECT_EQ(
1856 region["sz"].asString(),
1857 base::Uint64ToHexStringNoPrefix(static_cast<uint64_t>(kSizeKb * 1024)));
1858 EXPECT_EQ(region["id"].asString(), kModuleDebugid);
1859 EXPECT_EQ(region["df"].asString(), kModuleDebugPath);
1860 EXPECT_EQ(region["bs"]["pc"].asString(),
1861 base::Uint64ToHexStringNoPrefix(
1862 static_cast<uint64_t>(kPrivateCleanResidentKb * 1024)));
1863 EXPECT_EQ(region["bs"]["pd"].asString(),
1864 base::Uint64ToHexStringNoPrefix(
1865 static_cast<uint64_t>(kPrivateDirtyKb * 1024)));
1866 EXPECT_EQ(region["bs"]["pss"].asString(),
1867 base::Uint64ToHexStringNoPrefix(
1868 static_cast<uint64_t>(kProportionalResidentKb * 1024)));
1869 EXPECT_EQ(region["bs"]["sc"].asString(),
1870 base::Uint64ToHexStringNoPrefix(
1871 static_cast<uint64_t>(kSharedCleanResidentKb * 1024)));
1872 EXPECT_EQ(region["bs"]["sd"].asString(),
1873 base::Uint64ToHexStringNoPrefix(
1874 static_cast<uint64_t>(kSharedDirtyResidentKb * 1024)));
1875 EXPECT_EQ(
1876 region["bs"]["sw"].asString(),
1877 base::Uint64ToHexStringNoPrefix(static_cast<uint64_t>(kSwapKb * 1024)));
1878 }
1879
TEST_F(ExportJsonTest,MemorySnapshotChromeDumpEvent)1880 TEST_F(ExportJsonTest, MemorySnapshotChromeDumpEvent) {
1881 const int64_t kTimestamp = 10000000;
1882 const int64_t kSize = 1000;
1883 const int64_t kEffectiveSize = 2000;
1884 const int64_t kScalarAttrValue = 3000;
1885 const uint32_t kOsProcessID = 100;
1886 const uint32_t kChromeProcessID = 200;
1887 const uint32_t kImportance = 1;
1888 const char* kLevelOfDetail = "detailed";
1889 const char* kPath1 = "path/to_file1";
1890 const char* kPath2 = "path/to_file2";
1891 const char* kScalarAttrUnits = "scalar_units";
1892 const char* kStringAttrValue = "string_value";
1893 const std::string kScalarAttrName = "scalar_name";
1894 const std::string kStringAttrName = "string_name";
1895
1896 UniquePid os_upid =
1897 context_.process_tracker->GetOrCreateProcess(kOsProcessID);
1898 TrackId track = context_.track_tracker->InternProcessTrack(os_upid);
1899 StringId level_of_detail_id =
1900 context_.storage->InternString(base::StringView(kLevelOfDetail));
1901 auto snapshot_id = context_.storage->mutable_memory_snapshot_table()
1902 ->Insert({kTimestamp, track, level_of_detail_id})
1903 .id;
1904
1905 UniquePid chrome_upid =
1906 context_.process_tracker->GetOrCreateProcess(kChromeProcessID);
1907 auto process_id = context_.storage->mutable_process_memory_snapshot_table()
1908 ->Insert({snapshot_id, chrome_upid})
1909 .id;
1910
1911 StringId path1_id = context_.storage->InternString(base::StringView(kPath1));
1912 StringId path2_id = context_.storage->InternString(base::StringView(kPath2));
1913 SnapshotNodeId node1_id =
1914 context_.storage->mutable_memory_snapshot_node_table()
1915 ->Insert(
1916 {process_id, SnapshotNodeId(0), path1_id, kSize, kEffectiveSize})
1917 .id;
1918 SnapshotNodeId node2_id =
1919 context_.storage->mutable_memory_snapshot_node_table()
1920 ->Insert({process_id, SnapshotNodeId(0), path2_id, 0, 0})
1921 .id;
1922
1923 context_.args_tracker->AddArgsTo(node1_id).AddArg(
1924 context_.storage->InternString(
1925 base::StringView(kScalarAttrName + ".value")),
1926 Variadic::Integer(kScalarAttrValue));
1927 context_.args_tracker->AddArgsTo(node1_id).AddArg(
1928 context_.storage->InternString(
1929 base::StringView(kScalarAttrName + ".unit")),
1930 Variadic::String(context_.storage->InternString(kScalarAttrUnits)));
1931 context_.args_tracker->AddArgsTo(node1_id).AddArg(
1932 context_.storage->InternString(
1933 base::StringView(kStringAttrName + ".value")),
1934 Variadic::String(context_.storage->InternString(kStringAttrValue)));
1935 context_.args_tracker->Flush();
1936
1937 context_.storage->mutable_memory_snapshot_edge_table()->Insert(
1938 {node1_id, node2_id, kImportance});
1939
1940 base::TempFile temp_file = base::TempFile::Create();
1941 FILE* output = fopen(temp_file.path().c_str(), "w+");
1942 util::Status status = ExportJson(context_.storage.get(), output);
1943
1944 EXPECT_TRUE(status.ok());
1945
1946 Json::Value result = ToJsonValue(ReadFile(output));
1947 EXPECT_EQ(result["traceEvents"].size(), 1u);
1948
1949 Json::Value event = result["traceEvents"][0];
1950 EXPECT_EQ(event["ph"].asString(), "v");
1951 EXPECT_EQ(event["cat"].asString(), "disabled-by-default-memory-infra");
1952 EXPECT_EQ(event["id"].asString(), base::Uint64ToHexString(snapshot_id.value));
1953 EXPECT_EQ(event["ts"].asInt64(), kTimestamp / 1000);
1954 EXPECT_EQ(event["name"].asString(), "periodic_interval");
1955 EXPECT_EQ(event["pid"].asUInt(), kChromeProcessID);
1956 EXPECT_EQ(event["tid"].asInt(), -1);
1957
1958 EXPECT_TRUE(event["args"].isObject());
1959 EXPECT_EQ(event["args"]["dumps"]["level_of_detail"].asString(),
1960 kLevelOfDetail);
1961
1962 EXPECT_EQ(event["args"]["dumps"]["allocators"].size(), 2u);
1963 Json::Value node1 = event["args"]["dumps"]["allocators"][kPath1];
1964 EXPECT_TRUE(node1.isObject());
1965 EXPECT_EQ(
1966 node1["guid"].asString(),
1967 base::Uint64ToHexStringNoPrefix(static_cast<uint64_t>(node1_id.value)));
1968 EXPECT_TRUE(node1["attrs"]["size"].isObject());
1969 EXPECT_EQ(node1["attrs"]["size"]["value"].asString(),
1970 base::Uint64ToHexStringNoPrefix(static_cast<uint64_t>(kSize)));
1971 EXPECT_EQ(node1["attrs"]["size"]["type"].asString(), "scalar");
1972 EXPECT_EQ(node1["attrs"]["size"]["units"].asString(), "bytes");
1973 EXPECT_EQ(
1974 node1["attrs"]["effective_size"]["value"].asString(),
1975 base::Uint64ToHexStringNoPrefix(static_cast<uint64_t>(kEffectiveSize)));
1976 EXPECT_TRUE(node1["attrs"][kScalarAttrName].isObject());
1977 EXPECT_EQ(
1978 node1["attrs"][kScalarAttrName]["value"].asString(),
1979 base::Uint64ToHexStringNoPrefix(static_cast<uint64_t>(kScalarAttrValue)));
1980 EXPECT_EQ(node1["attrs"][kScalarAttrName]["type"].asString(), "scalar");
1981 EXPECT_EQ(node1["attrs"][kScalarAttrName]["units"].asString(),
1982 kScalarAttrUnits);
1983 EXPECT_TRUE(node1["attrs"][kStringAttrName].isObject());
1984 EXPECT_EQ(node1["attrs"][kStringAttrName]["value"].asString(),
1985 kStringAttrValue);
1986 EXPECT_EQ(node1["attrs"][kStringAttrName]["type"].asString(), "string");
1987 EXPECT_EQ(node1["attrs"][kStringAttrName]["units"].asString(), "");
1988
1989 Json::Value node2 = event["args"]["dumps"]["allocators"][kPath2];
1990 EXPECT_TRUE(node2.isObject());
1991 EXPECT_EQ(
1992 node2["guid"].asString(),
1993 base::Uint64ToHexStringNoPrefix(static_cast<uint64_t>(node2_id.value)));
1994 EXPECT_TRUE(node2["attrs"].empty());
1995
1996 Json::Value graph = event["args"]["dumps"]["allocators_graph"];
1997 EXPECT_TRUE(graph.isArray());
1998 EXPECT_EQ(graph.size(), 1u);
1999 EXPECT_EQ(
2000 graph[0]["source"].asString(),
2001 base::Uint64ToHexStringNoPrefix(static_cast<uint64_t>(node1_id.value)));
2002 EXPECT_EQ(
2003 graph[0]["target"].asString(),
2004 base::Uint64ToHexStringNoPrefix(static_cast<uint64_t>(node2_id.value)));
2005 EXPECT_EQ(graph[0]["importance"].asUInt(), kImportance);
2006 EXPECT_EQ(graph[0]["type"].asString(), "ownership");
2007 }
2008
2009 } // namespace
2010 } // namespace json
2011 } // namespace trace_processor
2012 } // namespace perfetto
2013