• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include <cstdio>
17 #include <fstream>
18 #include <fcntl.h>
19 
20 #include "ecmascript/dfx/hprof/heap_profiler_interface.h"
21 #include "ecmascript/dfx/hprof/heap_profiler.h"
22 #include "ecmascript/dfx/hprof/heap_snapshot_json_serializer.h"
23 #include "ecmascript/dfx/hprof/heap_snapshot.h"
24 #include "ecmascript/ecma_string.h"
25 #include "ecmascript/global_env.h"
26 
27 #include "ecmascript/js_tagged_value.h"
28 #include "ecmascript/js_thread.h"
29 #include "ecmascript/mem/heap.h"
30 #include "ecmascript/tests/test_helper.h"
31 #include "ecmascript/dfx/hprof/file_stream.h"
32 
33 using namespace panda::ecmascript;
34 
35 namespace panda::ecmascript {
36 class TestProgress : public Progress {
37 public:
38     TestProgress() = default;
39     ~TestProgress() = default;
40 
ReportProgress(int32_t done,int32_t total)41     void ReportProgress([[maybe_unused]] int32_t done, [[maybe_unused]] int32_t total) override {}
42 };
43 
44 class TestStream : public Stream {
45 public:
46     TestStream() = default;
47     ~TestStream() = default;
48 
EndOfStream()49     void EndOfStream() override {}
GetSize()50     int GetSize() override
51     {
52         static const int heapProfilerChunkSise = 100_KB;
53         return heapProfilerChunkSise;
54     }
WriteChunk(char * data,int32_t size)55     bool WriteChunk([[maybe_unused]] char *data, [[maybe_unused]] int32_t size) override
56     {
57         return true;
58     }
Good()59     bool Good() override
60     {
61         return testStream_.good();
62     }
63 
UpdateHeapStats(HeapStat * updateData,int32_t count)64     void UpdateHeapStats([[maybe_unused]] HeapStat* updateData, [[maybe_unused]] int32_t count) override
65     {
66     }
67 
UpdateLastSeenObjectId(int32_t lastSeenObjectId,int64_t timeStampUs)68     void UpdateLastSeenObjectId([[maybe_unused]] int32_t lastSeenObjectId, [[maybe_unused]]int64_t timeStampUs) override
69     {
70     }
71 
Clear()72     void Clear()
73     {
74         testStream_.clear(std::ios::badbit);
75     }
76 
77 private:
78     std::fstream testStream_;
79 };
80 }
81 
82 namespace panda::test {
83 class HeapTrackerTest : public testing::Test {
84 public:
SetUpTestCase()85     static void SetUpTestCase()
86     {
87         GTEST_LOG_(INFO) << "SetUpTestCase";
88     }
89 
TearDownTestCase()90     static void TearDownTestCase()
91     {
92         GTEST_LOG_(INFO) << "TearDownCase";
93     }
94 
SetUp()95     void SetUp() override
96     {
97         TestHelper::CreateEcmaVMWithScope(instance, thread, scope);
98         instance->SetEnableForceGC(false);
99     }
100 
TearDown()101     void TearDown() override
102     {
103         TestHelper::DestroyEcmaVMWithScope(instance, scope);
104     }
105 
106     EcmaVM *instance {nullptr};
107     EcmaHandleScope *scope {nullptr};
108     JSThread *thread {nullptr};
109 };
110 
HWTEST_F_L0(HeapTrackerTest,HeapTracker)111 HWTEST_F_L0(HeapTrackerTest, HeapTracker)
112 {
113     [[maybe_unused]] EcmaHandleScope handleScope(thread);
114     HeapProfilerInterface *heapProfile = HeapProfilerInterface::GetInstance(instance);
115     heapProfile->StartHeapTracking(50);
116     sleep(1);
117     int count = 100;
118     while (count-- > 0) {
119         instance->GetFactory()->NewJSAsyncFuncObject();
120     }
121     sleep(1);
122     count = 100;
123     while (count-- > 0) {
124         instance->GetFactory()->NewJSSymbol();
125     }
126     sleep(1);
127     count = 100;
128     while (count-- > 0) {
129         JSHandle<JSTaggedValue> undefined = instance->GetJSThread()->GlobalConstants()->GetHandledUndefined();
130         JSHandle<EcmaString> string = instance->GetFactory()->NewFromASCII("Hello World");
131         instance->GetFactory()->NewJSString(JSHandle<JSTaggedValue>(string), undefined);
132     }
133 
134     // Create file test.heaptimeline
135     std::string fileName = "test.heaptimeline";
136     fstream outputString(fileName, std::ios::out);
137     outputString.close();
138     outputString.clear();
139 
140     FileStream stream(fileName.c_str());
141     heapProfile->StopHeapTracking(&stream, nullptr);
142     HeapProfilerInterface::Destroy(instance);
143 
144     // Check
145     fstream inputStream(fileName, std::ios::in);
146     std::string line;
147     std::string emptySample = "\"samples\":";
148     std::string firstSample = "\"samples\":[0, ";
149     uint32_t emptySize = emptySample.size();
150     bool isFind = false;
151     while (getline(inputStream, line)) {
152         if (line.substr(0U, emptySize) == emptySample) {
153             ASSERT_TRUE(line.substr(0, firstSample.size()) == firstSample);
154             isFind = true;
155         }
156     }
157     ASSERT_TRUE(isFind);
158 
159     inputStream.close();
160     inputStream.clear();
161     std::remove(fileName.c_str());
162 }
163 
HWTEST_F_L0(HeapTrackerTest,HeapTrackerTraceAllocation)164 HWTEST_F_L0(HeapTrackerTest, HeapTrackerTraceAllocation)
165 {
166     [[maybe_unused]] EcmaHandleScope handleScope(thread);
167     HeapProfilerInterface *heapProfile = HeapProfilerInterface::GetInstance(instance);
168     TestStream testStream;
169     heapProfile->StartHeapTracking(50, true, &testStream, true);
170     sleep(1);
171     int count = 100;
172     while (count-- > 0) {
173         instance->GetFactory()->NewJSAsyncFuncObject();
174     }
175     sleep(1);
176     count = 100;
177     while (count-- > 0) {
178         instance->GetFactory()->NewJSSymbol();
179     }
180     sleep(1);
181     count = 100;
182     while (count-- > 0) {
183         JSHandle<JSTaggedValue> undefined = instance->GetJSThread()->GlobalConstants()->GetHandledUndefined();
184         JSHandle<EcmaString> string = instance->GetFactory()->NewFromASCII("Hello World");
185         instance->GetFactory()->NewJSString(JSHandle<JSTaggedValue>(string), undefined);
186     }
187 
188     // Create file test.heaptimeline
189     std::string fileName = "test.heaptimeline";
190     fstream outputString(fileName, std::ios::out);
191     outputString.close();
192     outputString.clear();
193 
194     FileStream stream(fileName.c_str());
195     TestProgress testProgress;
196     heapProfile->StopHeapTracking(&stream, &testProgress);
197     HeapProfilerInterface::Destroy(instance);
198 
199     // Check
200     fstream inputStream(fileName, std::ios::in);
201     std::string line;
202     std::string emptyTraceFunctionInfo = "\"trace_function_infos\":[";
203     std::string firstTraceFunctionInfo = "\"trace_function_infos\":[0,";
204     uint32_t emptyTraceFunctionInfoSize = emptyTraceFunctionInfo.size();
205     bool traceFunctionInfoIsFind = false;
206     while (getline(inputStream, line)) {
207         if (line.substr(0U, emptyTraceFunctionInfoSize) == emptyTraceFunctionInfo) {
208             ASSERT_TRUE(line.substr(0, firstTraceFunctionInfo.size()) == firstTraceFunctionInfo);
209             traceFunctionInfoIsFind = true;
210             break;
211         }
212     }
213     ASSERT_TRUE(traceFunctionInfoIsFind);
214 
215     inputStream.close();
216     inputStream.clear();
217     std::remove(fileName.c_str());
218 }
219 
HWTEST_F_L0(HeapTrackerTest,DumpHeapSnapshot)220 HWTEST_F_L0(HeapTrackerTest, DumpHeapSnapshot)
221 {
222     [[maybe_unused]] EcmaHandleScope handleScope(thread);
223     HeapProfilerInterface *heapProfile = HeapProfilerInterface::GetInstance(instance);
224 
225     sleep(1);
226     int count = 100;
227     while (count-- > 0) {
228         instance->GetFactory()->NewJSAsyncFuncObject();
229     }
230     sleep(1);
231     count = 100;
232     while (count-- > 0) {
233         instance->GetFactory()->NewJSSymbol();
234     }
235     sleep(1);
236     count = 100;
237     while (count-- > 0) {
238         JSHandle<JSTaggedValue> undefined = instance->GetJSThread()->GlobalConstants()->GetHandledUndefined();
239         JSHandle<EcmaString> string = instance->GetFactory()->NewFromASCII("Hello World");
240         instance->GetFactory()->NewJSString(JSHandle<JSTaggedValue>(string), undefined);
241     }
242 
243     // Create file test.heaptimeline
244     std::string fileName = "test.heapsnapshot";
245     fstream outputString(fileName, std::ios::out);
246     outputString.close();
247     outputString.clear();
248 
249     FileStream stream(fileName.c_str());
250     TestProgress testProgress;
251     heapProfile->DumpHeapSnapshot(DumpFormat::JSON, &stream, &testProgress, true, true, false);
252     HeapProfilerInterface::Destroy(instance);
253 
254     // Check
255     fstream inputStream(fileName, std::ios::in);
256     std::string line;
257     std::string nodes = "\"nodes\":[";
258     std::string sample = "\"samples\":[]";
259     uint32_t nodesSize = nodes.size();
260     uint32_t sampleSize = sample.size();
261     bool isNodesFind = false;
262     bool isSampleFind = false;
263     while (getline(inputStream, line)) {
264         if (line.substr(0U, nodesSize) == nodes) {
265             isNodesFind = true;
266         }
267 
268         if (line.substr(0U, sampleSize) == sample) {
269             isSampleFind = true;
270         }
271     }
272     ASSERT_TRUE(isNodesFind);
273     ASSERT_TRUE(isSampleFind);
274 
275     inputStream.close();
276     inputStream.clear();
277     std::remove(fileName.c_str());
278 }
279 
HWTEST_F_L0(HeapTrackerTest,HeapSnapshotBuildUp)280 HWTEST_F_L0(HeapTrackerTest, HeapSnapshotBuildUp)
281 {
282     bool isVmMode = true;
283     bool isPrivate = false;
284     bool traceAllocation = false;
285     bool captureNumericValue = false;
286     HeapProfiler heapProfiler(instance);
287     HeapSnapshot heapSnapshot(instance, isVmMode, isPrivate, captureNumericValue, traceAllocation,
288                               heapProfiler.GetEntryIdMap(), instance->GetChunk());
289     EXPECT_TRUE(heapSnapshot.BuildUp());
290 }
291 
HWTEST_F_L0(HeapTrackerTest,HeapSnapshotUpdateNode)292 HWTEST_F_L0(HeapTrackerTest, HeapSnapshotUpdateNode)
293 {
294     bool isVmMode = true;
295     bool isPrivate = false;
296     bool traceAllocation = false;
297     bool captureNumericValue = false;
298     HeapProfiler heapProfiler(instance);
299     HeapSnapshot heapSnapshot(instance, isVmMode, isPrivate, captureNumericValue, traceAllocation,
300                               heapProfiler.GetEntryIdMap(), instance->GetChunk());
301     size_t beginNode = heapSnapshot.GetNodeCount();
302     heapSnapshot.UpdateNodes();
303     size_t endNode = heapSnapshot.GetNodeCount();
304     EXPECT_TRUE(beginNode != endNode);
305 }
306 
HWTEST_F_L0(HeapTrackerTest,GenDumpFileName_001)307 HWTEST_F_L0(HeapTrackerTest, GenDumpFileName_001)
308 {
309     [[maybe_unused]] EcmaHandleScope handleScope(thread);
310     HeapProfilerInterface *heapProfile = HeapProfilerInterface::GetInstance(instance);
311 
312     sleep(1);
313     int count = 100;
314     while (count-- > 0) {
315         instance->GetFactory()->NewJSAsyncFuncObject();
316     }
317     sleep(1);
318     count = 100;
319     while (count-- > 0) {
320         instance->GetFactory()->NewJSSymbol();
321     }
322     sleep(1);
323     count = 100;
324     while (count-- > 0) {
325         JSHandle<JSTaggedValue> undefined = instance->GetJSThread()->GlobalConstants()->GetHandledUndefined();
326         JSHandle<EcmaString> string = instance->GetFactory()->NewFromASCII("Hello World");
327         instance->GetFactory()->NewJSString(JSHandle<JSTaggedValue>(string), undefined);
328     }
329 
330     TestStream stream;
331     stream.Clear();
332     EXPECT_TRUE(!stream.Good());
333     TestProgress testProgress;
334     heapProfile->DumpHeapSnapshot(DumpFormat::JSON, &stream, &testProgress, true, true, false);
335     HeapProfilerInterface::Destroy(instance);
336 }
337 
HWTEST_F_L0(HeapTrackerTest,GenDumpFileName_002)338 HWTEST_F_L0(HeapTrackerTest, GenDumpFileName_002)
339 {
340     [[maybe_unused]] EcmaHandleScope handleScope(thread);
341     HeapProfilerInterface *heapProfile = HeapProfilerInterface::GetInstance(instance);
342 
343     sleep(1);
344     int count = 100;
345     while (count-- > 0) {
346         instance->GetFactory()->NewJSAsyncFuncObject();
347     }
348     sleep(1);
349     count = 100;
350     while (count-- > 0) {
351         instance->GetFactory()->NewJSSymbol();
352     }
353     sleep(1);
354     count = 100;
355     while (count-- > 0) {
356         JSHandle<JSTaggedValue> undefined = instance->GetJSThread()->GlobalConstants()->GetHandledUndefined();
357         JSHandle<EcmaString> string = instance->GetFactory()->NewFromASCII("Hello World");
358         instance->GetFactory()->NewJSString(JSHandle<JSTaggedValue>(string), undefined);
359     }
360 
361     TestStream stream;
362     stream.Clear();
363     EXPECT_TRUE(!stream.Good());
364     TestProgress testProgress;
365     heapProfile->DumpHeapSnapshot(DumpFormat::BINARY, &stream, &testProgress, true, true, false);
366     HeapProfilerInterface::Destroy(instance);
367 }
368 
HWTEST_F_L0(HeapTrackerTest,GenDumpFileName_003)369 HWTEST_F_L0(HeapTrackerTest, GenDumpFileName_003)
370 {
371     [[maybe_unused]] EcmaHandleScope handleScope(thread);
372     HeapProfilerInterface *heapProfile = HeapProfilerInterface::GetInstance(instance);
373 
374     sleep(1);
375     int count = 100;
376     while (count-- > 0) {
377         instance->GetFactory()->NewJSAsyncFuncObject();
378     }
379     sleep(1);
380     count = 100;
381     while (count-- > 0) {
382         instance->GetFactory()->NewJSSymbol();
383     }
384     sleep(1);
385     count = 100;
386     while (count-- > 0) {
387         JSHandle<JSTaggedValue> undefined = instance->GetJSThread()->GlobalConstants()->GetHandledUndefined();
388         JSHandle<EcmaString> string = instance->GetFactory()->NewFromASCII("Hello World");
389         instance->GetFactory()->NewJSString(JSHandle<JSTaggedValue>(string), undefined);
390     }
391 
392     TestStream stream;
393     stream.Clear();
394     EXPECT_TRUE(!stream.Good());
395     TestProgress testProgress;
396     heapProfile->DumpHeapSnapshot(DumpFormat::OTHER, &stream, &testProgress, true, true, false);
397     HeapProfilerInterface::Destroy(instance);
398 }
399 
HWTEST_F_L0(HeapTrackerTest,GenDumpFileName_004)400 HWTEST_F_L0(HeapTrackerTest, GenDumpFileName_004)
401 {
402     [[maybe_unused]] EcmaHandleScope handleScope(thread);
403     HeapProfilerInterface *heapProfile = HeapProfilerInterface::GetInstance(instance);
404 
405     sleep(1);
406     int count = 100;
407     while (count-- > 0) {
408         instance->GetFactory()->NewJSAsyncFuncObject();
409     }
410     sleep(1);
411     count = 100;
412     while (count-- > 0) {
413         instance->GetFactory()->NewJSSymbol();
414     }
415     sleep(1);
416     count = 100;
417     while (count-- > 0) {
418         JSHandle<JSTaggedValue> undefined = instance->GetJSThread()->GlobalConstants()->GetHandledUndefined();
419         JSHandle<EcmaString> string = instance->GetFactory()->NewFromASCII("Hello World");
420         instance->GetFactory()->NewJSString(JSHandle<JSTaggedValue>(string), undefined);
421     }
422 
423     TestStream stream;
424     stream.Clear();
425     EXPECT_TRUE(!stream.Good());
426     TestProgress testProgress;
427     DumpFormat dumFormat = static_cast<DumpFormat>(5);
428     heapProfile->DumpHeapSnapshot(dumFormat, &stream, &testProgress, true, true, false);
429     HeapProfilerInterface::Destroy(instance);
430 }
431 
HWTEST_F_L0(HeapTrackerTest,FileDescriptorStreamEndOfStream)432 HWTEST_F_L0(HeapTrackerTest, FileDescriptorStreamEndOfStream)
433 {
434     int fd = 3;
435     FileDescriptorStream fileStream(fd);
436     EXPECT_TRUE(fileStream.Good());
437     fileStream.EndOfStream();
438 }
439 
HWTEST_F_L0(HeapTrackerTest,StreamWriterEnd)440 HWTEST_F_L0(HeapTrackerTest, StreamWriterEnd)
441 {
442     [[maybe_unused]] EcmaHandleScope handleScope(thread);
443     HeapProfilerInterface *heapProfile = HeapProfilerInterface::GetInstance(instance);
444 
445     sleep(1);
446     int count = 100;
447     while (count-- > 0) {
448         instance->GetFactory()->NewJSAsyncFuncObject();
449     }
450     sleep(1);
451     count = 100;
452     while (count-- > 0) {
453         instance->GetFactory()->NewJSSymbol();
454     }
455     sleep(1);
456     count = 100;
457     while (count-- > 0) {
458         JSHandle<JSTaggedValue> undefined = instance->GetJSThread()->GlobalConstants()->GetHandledUndefined();
459         JSHandle<EcmaString> string = instance->GetFactory()->NewFromASCII("Hello World");
460         instance->GetFactory()->NewJSString(JSHandle<JSTaggedValue>(string), undefined);
461     }
462 
463     // Create file test.heaptimeline
464     std::string fileName = "test.heapsnapshot";
465     fstream outputString(fileName, std::ios::out);
466     outputString.close();
467     outputString.clear();
468 
469     FileStream stream(fileName.c_str());
470     CVector<HeapStat> statsBuffer;
471     statsBuffer.emplace_back(1, 2, 4);
472     stream.UpdateHeapStats(&statsBuffer.front(), static_cast<int32_t>(statsBuffer.size()));
473     stream.UpdateLastSeenObjectId(1, 1677567644913058);
474 
475     TestProgress testProgress;
476     heapProfile->DumpHeapSnapshot(DumpFormat::JSON, &stream, &testProgress, true, true, false);
477     StreamWriter streamWriter(&stream);
478     streamWriter.End();
479     HeapProfilerInterface::Destroy(instance);
480 
481     // Check
482     fstream inputStream(fileName, std::ios::in);
483     std::string line;
484     std::string nodes = "\"nodes\":[";
485     std::string sample = "\"samples\":[]";
486     uint32_t nodesSize = nodes.size();
487     uint32_t sampleSize = sample.size();
488     bool isNodesFind = false;
489     bool isSampleFind = false;
490     while (getline(inputStream, line)) {
491         if (line.substr(0U, nodesSize) == nodes) {
492             isNodesFind = true;
493         }
494 
495         if (line.substr(0U, sampleSize) == sample) {
496             isSampleFind = true;
497         }
498     }
499     ASSERT_TRUE(isNodesFind);
500     ASSERT_TRUE(isSampleFind);
501 
502     inputStream.close();
503     inputStream.clear();
504     std::remove(fileName.c_str());
505 }
506 
HWTEST_F_L0(HeapTrackerTest,GetStringByKey)507 HWTEST_F_L0(HeapTrackerTest, GetStringByKey)
508 {
509     StringKey key = static_cast<StringKey>(2);
510     StringHashMap stringHashMap(instance);
511     CString *hashMap = stringHashMap.GetStringByKey(key);
512     EXPECT_TRUE(hashMap == nullptr);
513 }
514 
HWTEST_F_L0(HeapTrackerTest,FormatString)515 HWTEST_F_L0(HeapTrackerTest, FormatString)
516 {
517     bool isVmMode = true;
518     bool isPrivate = false;
519     bool traceAllocation = false;
520     bool captureNumericValue = false;
521     HeapProfiler heapProfiler(instance);
522     HeapSnapshot heapSnapshot(instance, isVmMode, isPrivate, captureNumericValue, traceAllocation,
523                               heapProfiler.GetEntryIdMap(), instance->GetChunk());
524 
525     StringHashMap stringHashMap(instance);
526     CString ret = "H\"e\rl\nl\\o\t W\fo\31rld!";
527     stringHashMap.GetString(ret);
528     StringKey retKey = std::hash<std::string>{} (std::string(ret));
529 
530     CString *tmpResult = nullptr;
531     tmpResult = stringHashMap.GetStringByKey(retKey);
532     EXPECT_TRUE(tmpResult != nullptr);
533     EXPECT_TRUE(*tmpResult == "H`e`l`l`o` W`o`rld!");
534 }
535 
HWTEST_F_L0(HeapTrackerTest,FileDescriptorStreamWriteChunk)536 HWTEST_F_L0(HeapTrackerTest, FileDescriptorStreamWriteChunk)
537 {
538     int32_t fd = -1;
539     FileDescriptorStream testFileStream(fd);
540     CVector<HeapStat> statsBuffer;
541     statsBuffer.emplace_back(1, 2, 4);
542     testFileStream.UpdateHeapStats(&statsBuffer.front(), static_cast<int32_t>(statsBuffer.size()));
543     testFileStream.UpdateLastSeenObjectId(1, 1677567644913058);
544     testFileStream.GetSize();
545     std::string testString = "Hello!";
546     int strSize = testString.size();
547     bool isFileStream = testFileStream.WriteChunk(testString.data(), strSize);
548     EXPECT_TRUE(!isFileStream);
549 
550     fd = 5;
551     FileDescriptorStream tmpFileStream(fd);
552     tmpFileStream.Good();
553     testString = "Hello!";
554     strSize = testString.size();
555     isFileStream = tmpFileStream.WriteChunk(testString.data(), strSize);
556     close(fd);
557     EXPECT_TRUE(!isFileStream);
558 
559     std::string fileName = "test.StreamWriteChunk";
560     fd = open(fileName.c_str(), O_RDONLY);
561     if (fd < 0) {
562         fd = open(fileName.c_str(), O_RDWR | O_CREAT, S_IRWXU | S_IRWXG | S_IROTH);
563     }
564     FileDescriptorStream fileStream(fd);
565     testString = "Hello!";
566     strSize = testString.size();
567     isFileStream = fileStream.WriteChunk(testString.data(), strSize);
568     EXPECT_TRUE(isFileStream);
569     std::remove(fileName.c_str());
570     close(fd);
571 }
572 
HWTEST_F_L0(HeapTrackerTest,AddNodeToTree)573 HWTEST_F_L0(HeapTrackerTest, AddNodeToTree)
574 {
575     CVector<uint32_t> traceNodeIndex;
576     for (int i = 0; i < 3; i++) {
577         traceNodeIndex.push_back(i + 1);
578     }
579     TraceTree traceTree;
580     TraceNode *traceNode = traceTree.AddNodeToTree(traceNodeIndex);
581     EXPECT_TRUE(traceNode != nullptr);
582 }
583 
HWTEST_F_L0(HeapTrackerTest,FindOrAddChild)584 HWTEST_F_L0(HeapTrackerTest, FindOrAddChild)
585 {
586     TraceTree traceTree;
587     uint32_t index = 1;
588     TraceNode traceNode(&traceTree, index);
589     TraceNode *node = traceNode.FindOrAddChild(index);
590     EXPECT_TRUE(node->GetNodeIndex() == 1);
591 
592     TraceNode *tmpNode = traceNode.FindOrAddChild(2);
593     EXPECT_TRUE(tmpNode->GetNodeIndex() == 2);
594 }
595 }  // namespace panda::test
596