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