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