1 /*
2 * Copyright (C) 2024 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 "src/trace_processor/importers/proto/jit_tracker.h"
18
19 #include <cstdint>
20 #include <optional>
21 #include <string>
22
23 #include "perfetto/ext/base/string_view.h"
24 #include "perfetto/trace_processor/trace_blob_view.h"
25 #include "src/trace_processor/importers/common/address_range.h"
26 #include "src/trace_processor/importers/common/jit_cache.h"
27 #include "src/trace_processor/importers/common/mapping_tracker.h"
28 #include "src/trace_processor/importers/common/process_tracker.h"
29 #include "src/trace_processor/importers/common/stack_profile_tracker.h"
30 #include "src/trace_processor/storage/stats.h"
31 #include "src/trace_processor/storage/trace_storage.h"
32 #include "src/trace_processor/tables/jit_tables_py.h"
33 #include "src/trace_processor/util/build_id.h"
34 #include "test/gtest_and_gmock.h"
35
36 namespace perfetto {
37 namespace trace_processor {
38 namespace {
39
40 using ::testing::_;
41 using ::testing::DoAll;
42 using ::testing::Eq;
43 using ::testing::FieldsAre;
44 using ::testing::IsEmpty;
45 using ::testing::Ne;
46 using ::testing::Optional;
47 using ::testing::SaveArg;
48
49 class JitTrackerTest : public testing::Test {
50 public:
JitTrackerTest()51 JitTrackerTest() {
52 context_.storage.reset(new TraceStorage());
53 context_.stack_profile_tracker.reset(new StackProfileTracker(&context_));
54 context_.mapping_tracker.reset(new MappingTracker(&context_));
55 context_.process_tracker.reset(new ProcessTracker(&context_));
56 jit_tracker_ = JitTracker::GetOrCreate(&context_);
57 }
58
59 protected:
AddMapping(UniquePid upid,AddressRange range,uint64_t exact_offset=0,uint64_t load_bias=0)60 UserMemoryMapping& AddMapping(UniquePid upid,
61 AddressRange range,
62 uint64_t exact_offset = 0,
63 uint64_t load_bias = 0) {
64 uint32_t id = context_.storage->stack_profile_mapping_table().row_count();
65 CreateMappingParams params;
66 params.memory_range = range;
67 params.build_id =
68 BuildId::FromRaw(reinterpret_cast<const uint8_t*>(&id), sizeof(id));
69 params.exact_offset = exact_offset;
70 params.start_offset = exact_offset;
71 params.load_bias = load_bias;
72 params.name = "Mapping ";
73 params.name += std::to_string(id);
74 return context_.mapping_tracker->CreateUserMemoryMapping(upid,
75 std::move(params));
76 }
77
78 TraceProcessorContext context_;
79 JitTracker* jit_tracker_;
80 };
81
TEST_F(JitTrackerTest,BasicFunctionality)82 TEST_F(JitTrackerTest, BasicFunctionality) {
83 const UniquePid upid = context_.process_tracker->GetOrCreateProcess(1234);
84 const UniqueTid utid = context_.process_tracker->UpdateThread(4321, 1234);
85 const AddressRange jit_range(0, 1000);
86 auto& mapping = AddMapping(upid, jit_range);
87 JitCache* cache = jit_tracker_->CreateJitCache("name", upid, jit_range);
88
89 const StringId function_name = context_.storage->InternString("Function 1");
90 const StringId source_file = context_.storage->InternString("SourceFile");
91 const int64_t create_ts = 12345;
92 const AddressRange code_range(0, 100);
93
94 auto code_id = cache->LoadCode(create_ts, utid, code_range, function_name,
95 JitCache::SourceLocation{source_file, 10},
96 TraceBlobView());
97
98 auto code = *context_.storage->jit_code_table().FindById(code_id);
99 EXPECT_THAT(code.create_ts(), Eq(create_ts));
100 EXPECT_THAT(code.estimated_delete_ts(), Eq(std::nullopt));
101 EXPECT_THAT(code.utid(), Eq(utid));
102 EXPECT_THAT(code.start_address(),
103 Eq(static_cast<int64_t>(code_range.start())));
104 EXPECT_THAT(code.size(), Eq(static_cast<int64_t>(code_range.size())));
105 EXPECT_THAT(code.function_name(), Eq(function_name));
106
107 auto frame_id = mapping.InternFrame(50, "");
108
109 auto frame =
110 *context_.storage->stack_profile_frame_table().FindById(frame_id);
111 EXPECT_THAT(frame.name(), Eq(function_name));
112
113 auto row = context_.storage->jit_frame_table().FindById(
114 tables::JitFrameTable::Id(0));
115 ASSERT_THAT(row, Ne(std::nullopt));
116
117 EXPECT_THAT(row->jit_code_id(), Eq(code_id));
118 EXPECT_THAT(row->frame_id(), Eq(frame_id));
119 }
120
TEST_F(JitTrackerTest,FunctionOverlapUpdatesDeleteTs)121 TEST_F(JitTrackerTest, FunctionOverlapUpdatesDeleteTs) {
122 const UniquePid upid = context_.process_tracker->GetOrCreateProcess(1234);
123 const UniqueTid utid = context_.process_tracker->UpdateThread(4321, 1234);
124 const AddressRange jit_range(0, 1000);
125 auto& mapping = AddMapping(upid, jit_range);
126 JitCache* cache = jit_tracker_->CreateJitCache("name", upid, jit_range);
127
128 const StringId function_name_1 = context_.storage->InternString("Function 1");
129 const StringId function_name_2 = context_.storage->InternString("Function 2");
130 const StringId source_file = context_.storage->InternString("SourceFile");
131 const int64_t create_ts_1 = 12345;
132 const int64_t create_ts_2 = 23456;
133 const AddressRange code_range_1(0, 100);
134 const AddressRange code_range_2(50, 200);
135
136 auto code_id_1 = cache->LoadCode(
137 create_ts_1, utid, code_range_1, function_name_1,
138 JitCache::SourceLocation{source_file, 10}, TraceBlobView());
139 auto code_id_2 = cache->LoadCode(
140 create_ts_2, utid, code_range_2, function_name_2,
141 JitCache::SourceLocation{source_file, 10}, TraceBlobView());
142 EXPECT_THAT(code_id_1, Ne(code_id_2));
143
144 auto code_1 = *context_.storage->jit_code_table().FindById(code_id_1);
145 auto code_2 = *context_.storage->jit_code_table().FindById(code_id_2);
146
147 // Code 1 has been deleted
148 EXPECT_THAT(code_1.create_ts(), Eq(create_ts_1));
149 EXPECT_THAT(code_1.estimated_delete_ts(), Eq(create_ts_2));
150
151 // The only active code is 2 at this point.
152 EXPECT_THAT(code_2.create_ts(), Eq(create_ts_2));
153 EXPECT_THAT(code_2.estimated_delete_ts(), Eq(std::nullopt));
154
155 // No frame should mention code 1
156 FrameId frame_id = mapping.InternFrame(50, "");
157 auto frame_a =
158 *context_.storage->stack_profile_frame_table().FindById(frame_id);
159 EXPECT_THAT(frame_a.name(), Eq(function_name_2));
160 ASSERT_THAT(context_.storage->jit_frame_table().row_count(), Eq(1u));
161 auto row = context_.storage->jit_frame_table().FindById(
162 tables::JitFrameTable::Id(0));
163 EXPECT_THAT(row->jit_code_id(), Eq(code_id_2));
164 EXPECT_THAT(row->frame_id(), Eq(frame_id));
165
166 // Frames for the old code 1 must fail to resolve to a jitted function but
167 // still generate a frame.
168 EXPECT_THAT(context_.storage->stats().at(stats::jit_unknown_frame).value,
169 Eq(0));
170 frame_id = mapping.InternFrame(0, "custom");
171 EXPECT_THAT(context_.storage->stats().at(stats::jit_unknown_frame).value,
172 Eq(1));
173 auto frame_b =
174 *context_.storage->stack_profile_frame_table().FindById(frame_id);
175 EXPECT_THAT(frame_a.id(), Ne(frame_b.id()));
176 EXPECT_THAT(context_.storage->GetString(frame_b.name()), Eq("custom"));
177 EXPECT_THAT(context_.storage->jit_frame_table().row_count(), Eq(1u));
178 }
179
180 } // namespace
181
182 } // namespace trace_processor
183 } // namespace perfetto
184