1 : // Copyright 2015 Google Inc. All Rights Reserved.
2 : //
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 : #include "syzygy/grinder/grinders/mem_replay_grinder.h"
16 :
17 : #include "base/command_line.h"
18 : #include "base/files/file.h"
19 : #include "base/files/scoped_temp_dir.h"
20 : #include "base/strings/string_util.h"
21 : #include "gtest/gtest.h"
22 : #include "syzygy/bard/events/heap_alloc_event.h"
23 : #include "syzygy/bard/events/linked_event.h"
24 : #include "syzygy/pe/unittest_util.h"
25 :
26 : namespace grinder {
27 : namespace grinders {
28 :
29 : namespace {
30 :
31 : const char kHeapAlloc[] = "asan_HeapAlloc";
32 :
33 : class TestMemReplayGrinder : public MemReplayGrinder {
34 : public:
35 : // Types.
36 : using ProcessData = MemReplayGrinder::ProcessData;
37 :
38 : // Member variables.
39 : using MemReplayGrinder::function_enum_map_;
40 : using MemReplayGrinder::missing_events_;
41 : using MemReplayGrinder::parse_error_;
42 : using MemReplayGrinder::process_data_map_;
43 :
44 : // Member functions.
45 : using MemReplayGrinder::FindOrCreateProcessData;
46 : using MemReplayGrinder::FindOrCreateThreadData;
47 :
48 : // Creates and dispatches a TraceFunctionNameTableEntry event.
49 : void PlayFunctionNameTableEntry(uint32_t process_id,
50 : uint32_t function_id,
51 E : const base::StringPiece& function_name) {
52 : size_t buffer_size =
53 E : offsetof(TraceFunctionNameTableEntry, name) + function_name.size() + 1;
54 E : std::vector<uint8_t> buffer(buffer_size, 0);
55 E : auto data = reinterpret_cast<TraceFunctionNameTableEntry*>(buffer.data());
56 E : data->function_id = function_id;
57 E : data->name_length = function_name.size();
58 : // The name doesn't need to be null terminated as the length is explicitly
59 : // encoded. So using strncpy is perfectly safe.
60 E : ::strncpy(data->name, function_name.data(), function_name.size());
61 E : OnFunctionNameTableEntry(base::Time::Now(), process_id, data);
62 E : }
63 :
64 : // Creates and dispatches a heap alloc function call.
65 : void PlayHeapAllocCall(uint32_t process_id,
66 : uint32_t thread_id,
67 : uint64_t timestamp,
68 : uint32_t function_id,
69 : uint32_t stack_trace_id,
70 : HANDLE handle,
71 : DWORD flags,
72 : SIZE_T bytes,
73 E : LPVOID ret) {
74 : size_t arg_data_size = 5 * sizeof(uint32) + sizeof(handle) + sizeof(flags) +
75 E : sizeof(bytes) + sizeof(ret);
76 : size_t buffer_size =
77 E : offsetof(TraceDetailedFunctionCall, argument_data) + arg_data_size;
78 E : std::vector<uint8_t> buffer(buffer_size, 0);
79 E : auto data = reinterpret_cast<TraceDetailedFunctionCall*>(buffer.data());
80 E : data->timestamp = timestamp;
81 E : data->function_id = function_id;
82 E : data->stack_trace_id = stack_trace_id;
83 E : data->argument_data_size = arg_data_size;
84 :
85 : // Output the argument data.
86 E : uint8_t* cursor = data->argument_data;
87 E : *reinterpret_cast<uint32_t*>(cursor) = 4;
88 E : cursor += sizeof(uint32_t);
89 E : *reinterpret_cast<uint32_t*>(cursor) = sizeof(handle);
90 E : cursor += sizeof(uint32_t);
91 E : *reinterpret_cast<uint32_t*>(cursor) = sizeof(flags);
92 E : cursor += sizeof(uint32_t);
93 E : *reinterpret_cast<uint32_t*>(cursor) = sizeof(bytes);
94 E : cursor += sizeof(uint32_t);
95 E : *reinterpret_cast<uint32_t*>(cursor) = sizeof(ret);
96 E : cursor += sizeof(uint32_t);
97 E : *reinterpret_cast<HANDLE*>(cursor) = handle;
98 E : cursor += sizeof(HANDLE);
99 E : *reinterpret_cast<DWORD*>(cursor) = flags;
100 E : cursor += sizeof(DWORD);
101 E : *reinterpret_cast<SIZE_T*>(cursor) = bytes;
102 E : cursor += sizeof(SIZE_T);
103 E : *reinterpret_cast<LPVOID*>(cursor) = ret;
104 E : cursor += sizeof(LPVOID);
105 E : DCHECK_EQ(static_cast<ptrdiff_t>(arg_data_size),
106 : cursor - data->argument_data);
107 :
108 E : OnDetailedFunctionCall(base::Time::Now(), process_id, thread_id, data);
109 E : }
110 : };
111 :
112 : class MemReplayGrinderTest : public testing::Test {
113 : public:
114 E : MemReplayGrinderTest() : cmd_line_(base::FilePath(L"grinder.exe")) {}
115 :
116 : base::CommandLine cmd_line_;
117 : };
118 :
119 : } // namespace
120 :
121 E : TEST_F(MemReplayGrinderTest, ParseCommandLine) {
122 E : TestMemReplayGrinder grinder;
123 E : EXPECT_TRUE(grinder.function_enum_map_.empty());
124 E : EXPECT_TRUE(grinder.ParseCommandLine(&cmd_line_));
125 E : EXPECT_FALSE(grinder.function_enum_map_.empty());
126 E : }
127 :
128 E : TEST_F(MemReplayGrinderTest, RecognizedFunctionName) {
129 E : TestMemReplayGrinder grinder;
130 E : ASSERT_TRUE(grinder.ParseCommandLine(&cmd_line_));
131 E : grinder.PlayFunctionNameTableEntry(1, 1, kHeapAlloc);
132 E : EXPECT_EQ(1u, grinder.process_data_map_.size());
133 :
134 E : auto proc_data = grinder.FindOrCreateProcessData(1);
135 E : EXPECT_EQ(1u, proc_data->function_id_map.size());
136 E : auto it = proc_data->function_id_map.begin();
137 E : EXPECT_EQ(1u, it->first);
138 E : EXPECT_EQ(bard::EventInterface::EventType::kHeapAllocEvent, it->second);
139 E : }
140 :
141 E : TEST_F(MemReplayGrinderTest, UnrecognizedFunctionName) {
142 : static const char kDummyFunction[] = "DummyFunction";
143 E : TestMemReplayGrinder grinder;
144 E : ASSERT_TRUE(grinder.ParseCommandLine(&cmd_line_));
145 E : grinder.PlayFunctionNameTableEntry(1, 1, kDummyFunction);
146 E : EXPECT_TRUE(grinder.process_data_map_.empty());
147 E : EXPECT_EQ(1u, grinder.missing_events_.size());
148 E : EXPECT_STREQ(kDummyFunction, grinder.missing_events_.begin()->c_str());
149 E : }
150 :
151 E : TEST_F(MemReplayGrinderTest, NameBeforeCall) {
152 E : TestMemReplayGrinder grinder;
153 E : ASSERT_TRUE(grinder.ParseCommandLine(&cmd_line_));
154 :
155 E : const HANDLE kHandle = reinterpret_cast<HANDLE>(0xDEADBEEF);
156 E : const DWORD kFlags = 0xFF;
157 E : const SIZE_T kBytes = 247;
158 E : const LPVOID kRet = reinterpret_cast<LPVOID>(0xBAADF00D);
159 :
160 E : grinder.PlayFunctionNameTableEntry(1, 1, kHeapAlloc);
161 E : EXPECT_EQ(1u, grinder.process_data_map_.size());
162 E : auto proc_data = grinder.FindOrCreateProcessData(1);
163 E : EXPECT_EQ(1u, proc_data->function_id_map.size());
164 E : EXPECT_TRUE(proc_data->pending_function_ids.empty());
165 E : EXPECT_TRUE(proc_data->pending_calls.empty());
166 E : EXPECT_TRUE(proc_data->thread_data_map.empty());
167 :
168 : // The function call is processed immediately upon being seen.
169 E : grinder.PlayHeapAllocCall(1, 1, 0, 1, 0, kHandle, kFlags, kBytes, kRet);
170 E : EXPECT_EQ(1u, proc_data->function_id_map.size());
171 E : EXPECT_TRUE(proc_data->pending_function_ids.empty());
172 E : EXPECT_TRUE(proc_data->pending_calls.empty());
173 E : EXPECT_EQ(1u, proc_data->thread_data_map.size());
174 E : auto thread_data = grinder.FindOrCreateThreadData(proc_data, 1);
175 E : EXPECT_EQ(1u, thread_data->plot_line->size());
176 E : auto evt = (*thread_data->plot_line)[0];
177 E : EXPECT_EQ(bard::EventInterface::EventType::kHeapAllocEvent, evt->type());
178 E : auto ha = reinterpret_cast<const bard::events::HeapAllocEvent*>(&(*evt));
179 E : EXPECT_EQ(kHandle, ha->trace_heap());
180 E : EXPECT_EQ(kFlags, ha->flags());
181 E : EXPECT_EQ(kBytes, ha->bytes());
182 E : EXPECT_EQ(kRet, ha->trace_alloc());
183 E : }
184 :
185 E : TEST_F(MemReplayGrinderTest, CallBeforeName) {
186 E : TestMemReplayGrinder grinder;
187 E : ASSERT_TRUE(grinder.ParseCommandLine(&cmd_line_));
188 :
189 E : const HANDLE kHandle = reinterpret_cast<HANDLE>(0xDEADBEEF);
190 E : const DWORD kFlags = 0xFF;
191 E : const SIZE_T kBytes = 247;
192 E : const LPVOID kRet = reinterpret_cast<LPVOID>(0xBAADF00D);
193 :
194 : // The function call is seen before the corresponding function name is
195 : // defined so no parsing can happen. In this case the call should be
196 : // placed to the pending list.
197 E : grinder.PlayHeapAllocCall(1, 1, 0, 1, 0, kHandle, kFlags, kBytes, kRet);
198 E : EXPECT_EQ(1u, grinder.process_data_map_.size());
199 E : auto proc_data = grinder.FindOrCreateProcessData(1);
200 E : EXPECT_TRUE(proc_data->function_id_map.empty());
201 E : EXPECT_EQ(1u, proc_data->pending_function_ids.size());
202 E : EXPECT_EQ(1u, proc_data->pending_calls.size());
203 E : EXPECT_TRUE(proc_data->thread_data_map.empty());
204 :
205 : // And processed once the name is defined.
206 E : grinder.PlayFunctionNameTableEntry(1, 1, kHeapAlloc);
207 E : EXPECT_EQ(1u, proc_data->function_id_map.size());
208 E : EXPECT_TRUE(proc_data->pending_function_ids.empty());
209 E : EXPECT_TRUE(proc_data->pending_calls.empty());
210 E : EXPECT_EQ(1u, proc_data->thread_data_map.size());
211 E : auto thread_data = grinder.FindOrCreateThreadData(proc_data, 1);
212 E : EXPECT_EQ(1u, thread_data->plot_line->size());
213 E : auto evt = (*thread_data->plot_line)[0];
214 E : EXPECT_EQ(bard::EventInterface::EventType::kHeapAllocEvent, evt->type());
215 E : auto ha = reinterpret_cast<const bard::events::HeapAllocEvent*>(&(*evt));
216 E : EXPECT_EQ(kHandle, ha->trace_heap());
217 E : EXPECT_EQ(kFlags, ha->flags());
218 E : EXPECT_EQ(kBytes, ha->bytes());
219 E : EXPECT_EQ(kRet, ha->trace_alloc());
220 E : }
221 :
222 E : TEST_F(MemReplayGrinderTest, GrindHarnessTrace) {
223 E : TestMemReplayGrinder grinder;
224 E : EXPECT_TRUE(grinder.ParseCommandLine(&cmd_line_));
225 :
226 E : trace::parser::Parser parser;
227 E : ASSERT_TRUE(parser.Init(&grinder));
228 : base::FilePath trace_file =
229 E : testing::GetExeTestDataRelativePath(testing::kMemProfTraceFile);
230 E : ASSERT_TRUE(parser.OpenTraceFile(trace_file));
231 E : grinder.SetParser(&parser);
232 E : EXPECT_TRUE(parser.Consume());
233 E : EXPECT_FALSE(grinder.parse_error_);
234 :
235 : // Grind the data and expect there to be a single process entry that is
236 : // fully parsed.
237 E : EXPECT_TRUE(grinder.Grind());
238 E : EXPECT_EQ(1u, grinder.process_data_map_.size());
239 E : const auto& proc = grinder.process_data_map_.begin()->second;
240 E : EXPECT_TRUE(proc.pending_function_ids.empty());
241 E : EXPECT_TRUE(proc.story);
242 :
243 : // The story should consist of 3 plotlines, corresponding to the main thread
244 : // and the two worker threads.
245 E : EXPECT_EQ(3u, proc.story->plot_lines().size());
246 :
247 : // Find the plotline for thread 1 and thread 2 of the harness. The first has
248 : // 9 events, the second has 10.
249 E : bard::Story::PlotLine* pl1 = nullptr;
250 E : bard::Story::PlotLine* pl2 = nullptr;
251 E : for (size_t i = 0; i < proc.story->plot_lines().size(); ++i) {
252 E : if (pl1 == nullptr && proc.story->plot_lines()[i]->size() == 9)
253 E : pl1 = proc.story->plot_lines()[i];
254 E : else if (pl2 == nullptr && proc.story->plot_lines()[i]->size() == 10)
255 E : pl2 = proc.story->plot_lines()[i];
256 E : }
257 E : DCHECK(pl1 && pl2);
258 :
259 : // Validate the contents of plot line 1.
260 E : EXPECT_EQ((*pl1)[0]->type(), bard::EventInterface::kHeapCreateEvent);
261 E : EXPECT_EQ((*pl1)[1]->type(), bard::EventInterface::kHeapAllocEvent);
262 E : EXPECT_EQ((*pl1)[2]->type(), bard::EventInterface::kLinkedEvent);
263 E : EXPECT_EQ((*pl1)[3]->type(), bard::EventInterface::kLinkedEvent);
264 E : EXPECT_EQ((*pl1)[4]->type(), bard::EventInterface::kHeapAllocEvent);
265 E : EXPECT_EQ((*pl1)[5]->type(), bard::EventInterface::kHeapFreeEvent);
266 E : EXPECT_EQ((*pl1)[6]->type(), bard::EventInterface::kHeapSetInformationEvent);
267 E : EXPECT_EQ((*pl1)[7]->type(), bard::EventInterface::kHeapFreeEvent);
268 E : EXPECT_EQ((*pl1)[8]->type(), bard::EventInterface::kHeapDestroyEvent);
269 :
270 : // Validate the contents of plot line 2.
271 E : EXPECT_EQ((*pl2)[0]->type(), bard::EventInterface::kHeapCreateEvent);
272 E : EXPECT_EQ((*pl2)[1]->type(), bard::EventInterface::kHeapAllocEvent);
273 E : EXPECT_EQ((*pl2)[2]->type(), bard::EventInterface::kLinkedEvent);
274 E : EXPECT_EQ((*pl2)[3]->type(), bard::EventInterface::kLinkedEvent);
275 E : EXPECT_EQ((*pl2)[4]->type(), bard::EventInterface::kHeapReAllocEvent);
276 E : EXPECT_EQ((*pl2)[5]->type(), bard::EventInterface::kHeapFreeEvent);
277 E : EXPECT_EQ((*pl2)[6]->type(), bard::EventInterface::kHeapFreeEvent);
278 E : EXPECT_EQ((*pl2)[7]->type(), bard::EventInterface::kHeapDestroyEvent);
279 E : EXPECT_EQ((*pl2)[8]->type(), bard::EventInterface::kHeapFreeEvent);
280 E : EXPECT_EQ((*pl2)[9]->type(), bard::EventInterface::kHeapDestroyEvent);
281 :
282 : // Validate the dependencies between the two plot lines.
283 :
284 : bard::events::LinkedEvent* pl12 =
285 E : reinterpret_cast<bard::events::LinkedEvent*>((*pl1)[2]);
286 E : EXPECT_EQ(pl12->event()->type(), bard::EventInterface::kHeapCreateEvent);
287 E : EXPECT_TRUE(pl12->deps().empty());
288 :
289 : bard::events::LinkedEvent* pl13 =
290 E : reinterpret_cast<bard::events::LinkedEvent*>((*pl1)[3]);
291 E : EXPECT_EQ(pl13->event()->type(), bard::EventInterface::kHeapAllocEvent);
292 E : EXPECT_TRUE(pl13->deps().empty());
293 :
294 : bard::events::LinkedEvent* pl22 =
295 E : reinterpret_cast<bard::events::LinkedEvent*>((*pl2)[2]);
296 E : EXPECT_EQ(pl22->event()->type(), bard::EventInterface::kHeapAllocEvent);
297 E : EXPECT_EQ(1u, pl22->deps().size());
298 E : EXPECT_EQ((*pl1)[2], pl22->deps().front());
299 :
300 : bard::events::LinkedEvent* pl23 =
301 E : reinterpret_cast<bard::events::LinkedEvent*>((*pl2)[3]);
302 E : EXPECT_EQ(pl23->event()->type(), bard::EventInterface::kHeapSizeEvent);
303 E : EXPECT_EQ(1u, pl23->deps().size());
304 E : EXPECT_EQ((*pl1)[3], pl23->deps().front());
305 :
306 E : base::ScopedTempDir temp_dir;
307 E : ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
308 E : base::FilePath output_path = temp_dir.path().AppendASCII("output.bin");
309 E : base::ScopedFILE output_file(base::OpenFile(output_path, "wb"));
310 E : EXPECT_TRUE(grinder.OutputData(output_file.get()));
311 E : output_file.reset();
312 E : }
313 :
314 : } // namespace grinders
315 : } // namespace grinder
|