1 : // Copyright 2014 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 : // Function call logger unittests.
16 :
17 : #include "syzygy/agent/memprof/function_call_logger.h"
18 :
19 : #include "base/bind.h"
20 : #include "gmock/gmock.h"
21 : #include "gtest/gtest.h"
22 :
23 : namespace agent {
24 : namespace memprof {
25 :
26 : namespace {
27 :
28 : using trace::client::TraceFileSegment;
29 :
30 : // TODO(chrisha): Centralize this for use elsewhere.
31 : class TestRpcSession : public trace::client::RpcSession {
32 : public:
33 E : TestRpcSession() : closed_(false) {
34 E : }
35 :
36 i : virtual bool CreateSession(TraceFileSegment* segment) {
37 i : DCHECK_NE(static_cast<TraceFileSegment*>(nullptr), segment);
38 i : return AllocateBuffer(segment);
39 i : }
40 :
41 E : virtual bool AllocateBuffer(TraceFileSegment* segment) {
42 E : DCHECK_NE(static_cast<TraceFileSegment*>(nullptr), segment);
43 E : return AllocateBuffer(2 * 1024 * 1024, segment);
44 E : }
45 :
46 E : virtual bool AllocateBuffer(size_t min_size, TraceFileSegment* segment) {
47 E : DCHECK_NE(static_cast<TraceFileSegment*>(nullptr), segment);
48 E : if (closed_)
49 i : return false;
50 E : auto it = buffers_.insert(buffers_.begin(), std::vector<uint8>());
51 E : it->resize(min_size);
52 E : segment->base_ptr = it->data();
53 E : segment->buffer_info.buffer_offset = 0;
54 E : segment->buffer_info.buffer_size = min_size;
55 E : segment->buffer_info.shared_memory_handle = 0;
56 E : segment->end_ptr = segment->base_ptr + min_size;
57 : segment->header = reinterpret_cast<TraceFileSegmentHeader*>(
58 E : segment->base_ptr);
59 E : segment->write_ptr = reinterpret_cast<uint8*>(segment->header + 1);
60 E : segment->header->thread_id = ::GetCurrentThreadId();
61 E : segment->header->segment_length = 0;
62 E : return true;
63 E : }
64 :
65 i : virtual bool ExchangeBuffer(TraceFileSegment* segment) {
66 i : DCHECK_NE(static_cast<TraceFileSegment*>(nullptr), segment);
67 i : return AllocateBuffer(segment);
68 i : }
69 :
70 i : virtual bool ReturnBuffer(TraceFileSegment* segment) {
71 i : DCHECK_NE(static_cast<TraceFileSegment*>(nullptr), segment);
72 i : if (closed_)
73 i : return false;
74 i : segment->base_ptr = nullptr;
75 i : segment->buffer_info.buffer_offset = 0;
76 i : segment->buffer_info.buffer_size = 0;
77 i : segment->buffer_info.shared_memory_handle = 0;
78 i : segment->end_ptr = nullptr;
79 i : segment->header = nullptr;
80 i : segment->write_ptr = nullptr;
81 i : return true;
82 i : }
83 :
84 i : virtual bool CloseSession() {
85 i : if (closed_)
86 i : return false;
87 i : closed_ = true;
88 i : return true;
89 i : }
90 :
91 i : virtual void FreeSharedMemory() {
92 : return;
93 i : }
94 :
95 : private:
96 : // All generated buffers are kept around so that the contents of flushed
97 : // buffers can be inspected.
98 : std::list<std::vector<uint8>> buffers_;
99 : bool closed_;
100 : };
101 :
102 : class TestFunctionCallLogger : public FunctionCallLogger {
103 : public:
104 E : TestFunctionCallLogger()
105 : : FunctionCallLogger(&test_session_) {
106 : test_segment_.allocate_callback =
107 : base::Bind(&TestFunctionCallLogger::AllocateCallback,
108 E : base::Unretained(this));
109 E : test_session_.AllocateBuffer(&test_segment_);
110 E : }
111 :
112 : using FunctionCallLogger::function_id_map_;
113 : using FunctionCallLogger::emitted_stack_ids_;
114 :
115 : // The session and segment that are passed to the function call logger.
116 : TestRpcSession test_session_;
117 : TraceFileSegment test_segment_;
118 :
119 : // Stores information about the allocations made to test_segment.
120 : struct AllocationInfo {
121 : int record_type;
122 : size_t record_size;
123 : void* record;
124 : };
125 : std::vector<AllocationInfo> allocation_infos;
126 :
127 : // This callback is invoked whenever test_segment is written to.
128 : void AllocateCallback(
129 E : int record_type, size_t record_size, void* record) {
130 E : AllocationInfo info = { record_type, record_size, record };
131 E : allocation_infos.push_back(info);
132 E : }
133 : };
134 :
135 E : void TestEmitDetailedFunctionCall(TestFunctionCallLogger* fcl) {
136 E : ASSERT_NE(static_cast<TestFunctionCallLogger*>(nullptr), fcl);
137 E : EMIT_DETAILED_FUNCTION_CALL(fcl, &fcl->test_segment_, fcl);
138 E : }
139 :
140 : } // namespace
141 :
142 E : TEST(FunctionCallLoggerTest, TraceFunctionNameTableEntry) {
143 E : TestFunctionCallLogger fcl;
144 E : EXPECT_EQ(0u, fcl.function_id_map_.size());
145 :
146 E : std::string name("foo");
147 E : EXPECT_EQ(0u, fcl.GetFunctionId(&fcl.test_segment_, name));
148 E : EXPECT_EQ(1u, fcl.function_id_map_.size());
149 : EXPECT_THAT(fcl.function_id_map_,
150 E : testing::Contains(std::make_pair(name, 0)));
151 E : EXPECT_EQ(1u, fcl.allocation_infos.size());
152 :
153 E : const auto& info = fcl.allocation_infos.front();
154 : EXPECT_EQ(TraceFunctionNameTableEntry::kTypeId,
155 E : info.record_type);
156 E : EXPECT_TRUE(info.record != nullptr);
157 : TraceFunctionNameTableEntry* data =
158 : reinterpret_cast<TraceFunctionNameTableEntry*>(
159 E : info.record);
160 : EXPECT_LE(
161 : FIELD_OFFSET(TraceFunctionNameTableEntry, name) + data->name_length,
162 E : info.record_size);
163 E : EXPECT_EQ(name, data->name);
164 E : fcl.allocation_infos.empty();
165 :
166 : // Adding the same name again should do nothing.
167 E : EXPECT_EQ(0u, fcl.GetFunctionId(&fcl.test_segment_, "foo"));
168 E : EXPECT_EQ(1u, fcl.function_id_map_.size());
169 : EXPECT_THAT(fcl.function_id_map_,
170 E : testing::Contains(std::make_pair(std::string("foo"), 0)));
171 E : EXPECT_EQ(1u, fcl.allocation_infos.size());
172 E : }
173 :
174 E : TEST(FunctionCallLoggerTest, TraceStackTrace) {
175 E : TestFunctionCallLogger fcl;
176 E : EXPECT_EQ(0u, fcl.emitted_stack_ids_.size());
177 :
178 E : fcl.set_stack_trace_tracking(kTrackingNone);
179 E : EXPECT_EQ(0u, fcl.GetStackTraceId(&fcl.test_segment_));
180 E : EXPECT_EQ(0u, fcl.emitted_stack_ids_.size());
181 E : EXPECT_EQ(0u, fcl.allocation_infos.size());
182 :
183 E : fcl.set_stack_trace_tracking(kTrackingTrack);
184 E : EXPECT_NE(0u, fcl.GetStackTraceId(&fcl.test_segment_));
185 E : EXPECT_EQ(0u, fcl.emitted_stack_ids_.size());
186 E : EXPECT_EQ(0u, fcl.allocation_infos.size());
187 :
188 E : fcl.set_stack_trace_tracking(kTrackingEmit);
189 E : uint32 stack_trace_id = fcl.GetStackTraceId(&fcl.test_segment_);
190 E : EXPECT_NE(0u, stack_trace_id);
191 E : EXPECT_THAT(fcl.emitted_stack_ids_, testing::ElementsAre(stack_trace_id));
192 E : EXPECT_EQ(1u, fcl.allocation_infos.size());
193 E : const auto& info = fcl.allocation_infos[0];
194 E : EXPECT_EQ(TraceStackTrace::kTypeId, info.record_type);
195 E : EXPECT_TRUE(info.record != nullptr);
196 E : TraceStackTrace* data = reinterpret_cast<TraceStackTrace*>(info.record);
197 E : EXPECT_EQ(stack_trace_id, data->stack_trace_id);
198 E : EXPECT_LT(0u, data->num_frames);
199 : size_t expected_size = data->num_frames * sizeof(void*) +
200 E : FIELD_OFFSET(TraceStackTrace, frames);
201 E : EXPECT_LE(expected_size, info.record_size);
202 E : }
203 :
204 E : TEST(FunctionCallLoggerTest, TraceDetailedFunctionCall) {
205 E : TestFunctionCallLogger fcl;
206 E : fcl.set_stack_trace_tracking(kTrackingEmit);
207 E : EXPECT_EQ(0u, fcl.function_id_map_.size());
208 :
209 : std::string name("agent::memprof::`anonymous-namespace'::"
210 E : "TestEmitDetailedFunctionCall");
211 E : TestEmitDetailedFunctionCall(&fcl);
212 E : EXPECT_EQ(1u, fcl.function_id_map_.size());
213 : EXPECT_THAT(fcl.function_id_map_,
214 E : testing::Contains(std::make_pair(name, 0)));
215 E : EXPECT_EQ(3u, fcl.allocation_infos.size());
216 :
217 : // Validate that the name record was appropriately written.
218 E : const auto& info0 = fcl.allocation_infos[0];
219 : EXPECT_EQ(TraceFunctionNameTableEntry::kTypeId,
220 E : info0.record_type);
221 E : EXPECT_TRUE(info0.record != nullptr);
222 : TraceFunctionNameTableEntry* data0 =
223 : reinterpret_cast<TraceFunctionNameTableEntry*>(
224 E : info0.record);
225 : EXPECT_LE(
226 : FIELD_OFFSET(TraceFunctionNameTableEntry, name) + data0->name_length,
227 E : info0.record_size);
228 E : EXPECT_EQ(name, data0->name);
229 :
230 : // Validate that the stack trace was written correctly.
231 E : const auto& info1 = fcl.allocation_infos[1];
232 E : EXPECT_EQ(TraceStackTrace::kTypeId, info1.record_type);
233 E : EXPECT_TRUE(info1.record != nullptr);
234 E : TraceStackTrace* data1 = reinterpret_cast<TraceStackTrace*>(info1.record);
235 E : EXPECT_LT(0u, data1->num_frames);
236 : size_t expected_size = data1->num_frames * sizeof(void*) +
237 E : FIELD_OFFSET(TraceStackTrace, frames);
238 E : EXPECT_LE(expected_size, info1.record_size);
239 :
240 : // Validate that the detailed function call record was written correctly.
241 E : const auto& info2 = fcl.allocation_infos[2];
242 : EXPECT_EQ(TraceDetailedFunctionCall::kTypeId,
243 E : info2.record_type);
244 E : EXPECT_TRUE(info2.record != nullptr);
245 : TraceDetailedFunctionCall* data2 =
246 E : reinterpret_cast<TraceDetailedFunctionCall*>(info2.record);
247 : EXPECT_LE(
248 : FIELD_OFFSET(TraceDetailedFunctionCall, argument_data) +
249 : data2->argument_data_size,
250 E : info2.record_size);
251 E : EXPECT_EQ(0u, data2->function_id);
252 E : EXPECT_EQ(data1->stack_trace_id, data2->stack_trace_id);
253 E : EXPECT_NE(0ull, data2->timestamp);
254 : // Number of arguments, size of argument, content of argument.
255 E : EXPECT_EQ(2 * sizeof(uint32) + sizeof(void*), data2->argument_data_size);
256 :
257 E : void* fcl_ptr = &fcl;
258 E : uint8 ptr[4] = {};
259 E : ::memcpy(ptr, &fcl_ptr, sizeof(ptr));
260 : const uint8 kExpectedContents[] = {
261 : 0x01, 0x00, 0x00, 0x00, // 1 argument...
262 : 0x04, 0x00, 0x00, 0x00, // ...of size 4...
263 : ptr[0], ptr[1], ptr[2], ptr[3], // ...pointing to |fcl|.
264 E : };
265 E : ASSERT_EQ(arraysize(kExpectedContents), data2->argument_data_size);
266 : EXPECT_EQ(0u, ::memcmp(kExpectedContents, data2->argument_data,
267 E : data2->argument_data_size));
268 E : }
269 :
270 E : TEST(FunctionCallLoggerTest, TraceDetailedFunctionCallSerializeTimestamps) {
271 E : TestFunctionCallLogger fcl;
272 E : fcl.set_stack_trace_tracking(kTrackingNone);
273 E : fcl.set_serialize_timestamps(true);
274 E : EXPECT_EQ(0u, fcl.function_id_map_.size());
275 :
276 : std::string name("agent::memprof::`anonymous-namespace'::"
277 E : "TestEmitDetailedFunctionCall");
278 E : for (size_t i = 0; i < 3; ++i)
279 E : TestEmitDetailedFunctionCall(&fcl);
280 E : EXPECT_EQ(1u, fcl.function_id_map_.size());
281 : EXPECT_THAT(fcl.function_id_map_,
282 E : testing::Contains(std::make_pair(name, 0)));
283 : // 1 name, 3 calls.
284 E : EXPECT_EQ(4u, fcl.allocation_infos.size());
285 :
286 : // Validate that the name record was appropriately written.
287 E : const auto& info0 = fcl.allocation_infos[0];
288 : EXPECT_EQ(TraceFunctionNameTableEntry::kTypeId,
289 E : info0.record_type);
290 E : EXPECT_NE(nullptr, info0.record);
291 : TraceFunctionNameTableEntry* data0 =
292 : reinterpret_cast<TraceFunctionNameTableEntry*>(
293 E : info0.record);
294 : EXPECT_LE(
295 : FIELD_OFFSET(TraceFunctionNameTableEntry, name) + data0->name_length,
296 E : info0.record_size);
297 E : EXPECT_EQ(name, data0->name);
298 :
299 E : for (size_t i = 0; i < 3; ++i) {
300 : // Validate that the detailed function call has the appropriate timestamp.
301 E : const auto& info1 = fcl.allocation_infos[1 + i];
302 E : EXPECT_EQ(TraceDetailedFunctionCall::kTypeId, info1.record_type);
303 E : EXPECT_NE(nullptr, info0.record);
304 : TraceDetailedFunctionCall* data1 =
305 E : reinterpret_cast<TraceDetailedFunctionCall*>(info1.record);
306 E : EXPECT_EQ(static_cast<uint64>(i), data1->timestamp);
307 E : }
308 E : }
309 :
310 : } // namespace memprof
311 : } // namespace agent
|