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