Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(269)

Side by Side Diff: chrome/browser/metrics/call_stack_profile_metrics_provider_unittest.cc

Issue 981143006: Metrics provider for statistical stack profiler (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@lkcr
Patch Set: address comments Created 5 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/metrics/call_stack_profile_metrics_provider.h"
6
7 #include "base/memory/scoped_ptr.h"
8 #include "base/profiler/stack_sampling_profiler.h"
9 #include "base/strings/string_number_conversions.h"
10 #include "components/metrics/proto/chrome_user_metrics_extension.pb.h"
11 #include "testing/gtest/include/gtest/gtest.h"
12
13 using base::StackSamplingProfiler;
14 using Frame = StackSamplingProfiler::Frame;
15 using Module = StackSamplingProfiler::Module;
16 using Profile = StackSamplingProfiler::Profile;
17 using Sample = StackSamplingProfiler::Sample;
Ilya Sherman 2015/03/17 04:54:35 nit: Why the different syntax on lines 14-17 vs. l
Mike Wittman 2015/03/18 00:54:27 "using X::Y" is only valid if X is a namespace.
18
19 // Checks that all properties from multiple profiles are filled as expected.
20 TEST(CallStackProfileMetricsProviderTest, MultipleProfiles) {
21 const uintptr_t module1_base_address = 0x1000;
22 const uintptr_t module2_base_address = 0x2000;
23 const uintptr_t module3_base_address = 0x3000;
24
25 const Module profile_modules[][2] = {
26 {
27 {
28 reinterpret_cast<const void*>(module1_base_address),
29 "ABCD",
30 #if defined(OS_WIN)
Alexei Svitkine (slow) 2015/03/17 23:10:10 Do the actual paths matter? If not, I suggest sim
Mike Wittman 2015/03/18 01:48:36 Not that much. However, even if we use FILE_PATH_L
31 base::FilePath(L"c:\\some\\path\\to\\chrome.exe")
32 #else
33 base::FilePath("/some/path/to/chrome")
34 #endif
35 },
36 {
37 reinterpret_cast<const void*>(module2_base_address),
38 "EFGH",
39 #if defined(OS_WIN)
40 base::FilePath(L"c:\\some\\path\\to\\third_party.dll")
41 #else
42 base::FilePath("/some/path/to/third_party.so")
43 #endif
44 }
45 },
46 {
47 {
48 reinterpret_cast<const void*>(module3_base_address),
49 "MNOP",
50 #if defined(OS_WIN)
51 base::FilePath(L"c:\\some\\path\\to\\third_party2.dll")
52 #else
53 base::FilePath("/some/path/to/third_party2.so")
54 #endif
55 },
56 { // Repeated from the first profile.
57 reinterpret_cast<const void*>(module1_base_address),
58 "ABCD",
59 #if defined(OS_WIN)
60 base::FilePath(L"c:\\some\\path\\to\\chrome.exe")
61 #else
62 base::FilePath("/some/path/to/chrome")
63 #endif
64 },
65 }
66 };
67
68 // Values for Windows generated with:
69 // perl -MDigest::MD5=md5 -MEncode=encode \
70 // -e 'for(@ARGV){printf "%x\n", unpack "Q", md5 encode "UTF-16LE", $_}' \
71 // chrome.exe third_party.dll third_party2.dll
72 //
73 // Values for Linux generated with:
74 // perl -MDigest::MD5=md5 \
75 // -e 'for(@ARGV){printf "%x\n", unpack "Q", md5 $_}' \
76 // chrome third_party.so third_party2.so
77 const uint64 profile_expected_name_md5_prefixes[][2] = {
78 {
79 #if defined(OS_WIN)
80 0x2ac596616e4c346ULL,
81 0xba1aaedefd8b2b7eULL
82 #else
83 0x6cc31a45a8384855UL,
84 0xf8c9598614613684UL
85 #endif
86 },
87 {
88 #if defined(OS_WIN)
89 0xcad5a473456fb687ULL,
90 0x2ac596616e4c346ULL
91 #else
92 0x9eeca69f537e64b4UL,
93 0x6cc31a45a8384855UL
94 #endif
95 }
96 };
97
98 const Frame profile_sample_frames[][2][3] = {
Ilya Sherman 2015/03/17 04:54:35 It's not immediately clear to me what this variabl
Mike Wittman 2015/03/18 00:54:27 Done.
99 {
100 {
101 { reinterpret_cast<const void*>(module1_base_address + 0x10), 0 },
102 { reinterpret_cast<const void*>(module2_base_address + 0x20), 1 },
103 { reinterpret_cast<const void*>(module1_base_address + 0x30), 0 }
104 },
105 {
106 { reinterpret_cast<const void*>(module2_base_address + 0x10), 1 },
107 { reinterpret_cast<const void*>(module1_base_address + 0x20), 0 },
108 { reinterpret_cast<const void*>(module2_base_address + 0x30), 1 }
109 }
110 },
111 {
112 {
113 { reinterpret_cast<const void*>(module3_base_address + 0x10), 0 },
114 { reinterpret_cast<const void*>(module1_base_address + 0x20), 1 },
115 { reinterpret_cast<const void*>(module3_base_address + 0x30), 0 }
116 },
117 {
118 { reinterpret_cast<const void*>(module1_base_address + 0x10), 1 },
119 { reinterpret_cast<const void*>(module3_base_address + 0x20), 0 },
120 { reinterpret_cast<const void*>(module1_base_address + 0x30), 1 }
121 }
122 }
123 };
124
125 base::TimeDelta profile_durations[2] = {
126 base::TimeDelta::FromMilliseconds(100),
127 base::TimeDelta::FromMilliseconds(200)
128 };
129
130 base::TimeDelta profile_sampling_periods[2] = {
131 base::TimeDelta::FromMilliseconds(10),
132 base::TimeDelta::FromMilliseconds(20)
133 };
134
135 std::vector<Profile> profiles;
136 for (size_t i = 0; i < arraysize(profile_sample_frames); i++) {
Ilya Sherman 2015/03/17 04:54:35 nit: ++i, ++j below
Mike Wittman 2015/03/18 00:54:27 Done.
137 Profile profile;
138 profile.modules.insert(
139 profile.modules.end(), &profile_modules[i][0],
140 &profile_modules[i][0] + arraysize(profile_modules[i]));
141
142 for (size_t j = 0; j < arraysize(profile_sample_frames[i]); j++) {
143 profile.samples.push_back(Sample());
144 Sample& sample = profile.samples.back();
145 sample.insert(sample.end(), &profile_sample_frames[i][j][0],
146 &profile_sample_frames[i][j][0] +
147 arraysize(profile_sample_frames[i][j]));
148 }
149
150 profile.profile_duration = profile_durations[i];
151 profile.sampling_period = profile_sampling_periods[i];
152 profile.preserve_sample_ordering = false;
153
154 profiles.push_back(profile);
155 }
156
157 scoped_ptr<CallStackProfileMetricsProvider> provider(
158 new CallStackProfileMetricsProvider);
Ilya Sherman 2015/03/17 04:54:35 nit: Why not stack-allocate?
Mike Wittman 2015/03/18 00:54:27 Stack allocation works.
159 provider->SetSourceProfilesForTest(profiles);
160 metrics::ChromeUserMetricsExtension uma_proto;
161 provider->ProvideGeneralMetrics(&uma_proto);
162
163 ASSERT_EQ(static_cast<int>(arraysize(profile_sample_frames)),
164 uma_proto.sampled_profile().size());
165 for (size_t i = 0; i < arraysize(profile_sample_frames); i++) {
166 SCOPED_TRACE("profile " + base::IntToString(i));
167 const metrics::SampledProfile& sampled_profile =
168 uma_proto.sampled_profile().Get(i);
169 ASSERT_TRUE(sampled_profile.has_call_stack_profile());
170 const metrics::CallStackProfile& call_stack_profile =
171 sampled_profile.call_stack_profile();
172
173 ASSERT_EQ(static_cast<int>(arraysize(profile_sample_frames[i])),
174 call_stack_profile.sample().size());
175 for (size_t j = 0; j < arraysize(profile_sample_frames[i]); j++) {
176 SCOPED_TRACE("sample " + base::IntToString(j));
177 const metrics::CallStackProfile::Sample& proto_sample =
178 call_stack_profile.sample().Get(j);
179 ASSERT_EQ(static_cast<int>(arraysize(profile_sample_frames[i][j])),
180 proto_sample.entry().size());
181 ASSERT_TRUE(proto_sample.has_count());
182 EXPECT_EQ(1u, proto_sample.count());
183 for (size_t k = 0; k < arraysize(profile_sample_frames[i][j]); k++) {
184 SCOPED_TRACE("frame " + base::IntToString(k));
185 const metrics::CallStackEntry& entry = proto_sample.entry().Get(k);
186 ASSERT_TRUE(entry.has_address());
187 const char *instruction_pointer = reinterpret_cast<const char *>(
Ilya Sherman 2015/03/17 04:54:35 nit: "char *" -> "char* "
Mike Wittman 2015/03/18 00:54:27 Done.
188 profile_sample_frames[i][j][k].instruction_pointer);
189 const char *module_base_address = reinterpret_cast<const char *>(
190 profile_modules[i][profile_sample_frames[i][j][k].module_index]
191 .base_address);
192 EXPECT_EQ(static_cast<uint64>(instruction_pointer -
193 module_base_address), entry.address());
194 ASSERT_TRUE(entry.has_module_id_index());
195 EXPECT_EQ(profile_sample_frames[i][j][k].module_index,
196 entry.module_id_index());
197 }
198 }
199
200 ASSERT_EQ(static_cast<int>(arraysize(profile_modules[i])),
201 call_stack_profile.module_id().size());
202 for (size_t j = 0; j < arraysize(profile_modules[i]); j++) {
203 SCOPED_TRACE("module " + base::IntToString(j));
204 const metrics::ModuleIdentifier& module_identifier =
205 call_stack_profile.module_id().Get(j);
206 ASSERT_TRUE(module_identifier.has_build_id());
207 EXPECT_EQ(profile_modules[i][j].id, module_identifier.build_id());
208 ASSERT_TRUE(module_identifier.has_name_md5_prefix());
209 EXPECT_EQ(profile_expected_name_md5_prefixes[i][j],
210 module_identifier.name_md5_prefix());
211 }
212
213 ASSERT_TRUE(call_stack_profile.has_profile_duration_ms());
214 EXPECT_EQ(profile_durations[i].InMilliseconds(),
215 call_stack_profile.profile_duration_ms());
216 ASSERT_TRUE(call_stack_profile.has_sampling_period_ms());
217 EXPECT_EQ(profile_sampling_periods[i].InMilliseconds(),
218 call_stack_profile.sampling_period_ms());
219 }
220 }
Ilya Sherman 2015/03/17 04:54:35 This test is super long and has a lot of nested lo
Mike Wittman 2015/03/18 00:54:27 It is very long. But it also tests that there's no
221
222 // Checks that all duplicate samples are collapsed with
223 // preserve_sample_ordering = false.
224 TEST(CallStackProfileMetricsProviderTest, RepeatedStacksUnordered) {
225 const uintptr_t module_base_address = 0x1000;
226
227 const Module modules[] = {
228 {
229 reinterpret_cast<const void*>(module_base_address),
230 "ABCD",
231 #if defined(OS_WIN)
232 base::FilePath(L"c:\\some\\path\\to\\chrome.exe")
233 #else
234 base::FilePath("/some/path/to/chrome")
235 #endif
236 }
237 };
238
239 // Duplicate samples in slots 0, 2, and 3.
240 const Frame sample_frames[][1] = {
241 { { reinterpret_cast<const void*>(module_base_address + 0x10), 0 }, },
242 { { reinterpret_cast<const void*>(module_base_address + 0x20), 0 }, },
243 { { reinterpret_cast<const void*>(module_base_address + 0x10), 0 }, },
244 { { reinterpret_cast<const void*>(module_base_address + 0x10), 0 }, },
245 };
246
247 Profile profile;
248 profile.modules.insert(profile.modules.end(), &modules[0],
249 &modules[0] + arraysize(modules));
250
251 for (size_t i = 0; i < arraysize(sample_frames); i++) {
252 profile.samples.push_back(Sample());
253 Sample& sample = profile.samples.back();
254 sample.insert(sample.end(), &sample_frames[i][0],
255 &sample_frames[i][0] + arraysize(sample_frames[i]));
256 }
257
258 profile.profile_duration = base::TimeDelta::FromMilliseconds(100);
259 profile.sampling_period = base::TimeDelta::FromMilliseconds(10);
260 profile.preserve_sample_ordering = false;
261
262 scoped_ptr<CallStackProfileMetricsProvider> provider(
263 new CallStackProfileMetricsProvider);
264 provider->SetSourceProfilesForTest(std::vector<Profile>(1, profile));
265 metrics::ChromeUserMetricsExtension uma_proto;
266 provider->ProvideGeneralMetrics(&uma_proto);
267
268 ASSERT_EQ(1, uma_proto.sampled_profile().size());
269 const metrics::SampledProfile& sampled_profile =
270 uma_proto.sampled_profile().Get(0);
271 ASSERT_TRUE(sampled_profile.has_call_stack_profile());
272 const metrics::CallStackProfile& call_stack_profile =
273 sampled_profile.call_stack_profile();
274
275 ASSERT_EQ(2, call_stack_profile.sample().size());
276 for (int i = 0; i < 2; i++) {
277 SCOPED_TRACE("sample " + base::IntToString(i));
278 const metrics::CallStackProfile::Sample& proto_sample =
279 call_stack_profile.sample().Get(i);
280 ASSERT_EQ(static_cast<int>(arraysize(sample_frames[i])),
281 proto_sample.entry().size());
282 ASSERT_TRUE(proto_sample.has_count());
283 EXPECT_EQ(i == 0 ? 3u : 1u, proto_sample.count());
284 for (size_t j = 0; j < arraysize(sample_frames[i]); j++) {
285 SCOPED_TRACE("frame " + base::IntToString(j));
286 const metrics::CallStackEntry& entry = proto_sample.entry().Get(j);
287 ASSERT_TRUE(entry.has_address());
288 const char *instruction_pointer = reinterpret_cast<const char *>(
289 sample_frames[i][j].instruction_pointer);
290 const char *module_base_address = reinterpret_cast<const char *>(
291 modules[sample_frames[i][j].module_index].base_address);
292 EXPECT_EQ(static_cast<uint64>(instruction_pointer - module_base_address),
293 entry.address());
294 ASSERT_TRUE(entry.has_module_id_index());
295 EXPECT_EQ(sample_frames[i][j].module_index, entry.module_id_index());
296 }
297 }
298 }
299
300 // Checks that only contiguous duplicate samples are collapsed with
301 // preserve_sample_ordering = true.
302 TEST(CallStackProfileMetricsProviderTest, RepeatedStacksOrdered) {
303 const uintptr_t module_base_address = 0x1000;
304
305 const Module modules[] = {
306 {
307 reinterpret_cast<const void*>(module_base_address),
308 "ABCD",
309 #if defined(OS_WIN)
310 base::FilePath(L"c:\\some\\path\\to\\chrome.exe")
311 #else
312 base::FilePath("/some/path/to/chrome")
313 #endif
314 }
315 };
316
317 // Duplicate samples in slots 0, 2, and 3.
318 const Frame sample_frames[][1] = {
319 { { reinterpret_cast<const void*>(module_base_address + 0x10), 0 }, },
320 { { reinterpret_cast<const void*>(module_base_address + 0x20), 0 }, },
321 { { reinterpret_cast<const void*>(module_base_address + 0x10), 0 }, },
322 { { reinterpret_cast<const void*>(module_base_address + 0x10), 0 }, }
323 };
324
325 Profile profile;
326 profile.modules.insert(profile.modules.end(), &modules[0],
327 &modules[0] + arraysize(modules));
328
329 for (size_t i = 0; i < arraysize(sample_frames); i++) {
330 profile.samples.push_back(Sample());
331 Sample& sample = profile.samples.back();
332 sample.insert(sample.end(), &sample_frames[i][0],
333 &sample_frames[i][0] + arraysize(sample_frames[i]));
334 }
335
336 profile.profile_duration = base::TimeDelta::FromMilliseconds(100);
337 profile.sampling_period = base::TimeDelta::FromMilliseconds(10);
338 profile.preserve_sample_ordering = true;
339
340 scoped_ptr<CallStackProfileMetricsProvider> provider(
341 new CallStackProfileMetricsProvider);
342 provider->SetSourceProfilesForTest(std::vector<Profile>(1, profile));
343 metrics::ChromeUserMetricsExtension uma_proto;
344 provider->ProvideGeneralMetrics(&uma_proto);
345
346 ASSERT_EQ(1, uma_proto.sampled_profile().size());
347 const metrics::SampledProfile& sampled_profile =
348 uma_proto.sampled_profile().Get(0);
349 ASSERT_TRUE(sampled_profile.has_call_stack_profile());
350 const metrics::CallStackProfile& call_stack_profile =
351 sampled_profile.call_stack_profile();
352
353 ASSERT_EQ(3, call_stack_profile.sample().size());
354 for (int i = 0; i < 3; i++) {
355 SCOPED_TRACE("sample " + base::IntToString(i));
356 const metrics::CallStackProfile::Sample& proto_sample =
357 call_stack_profile.sample().Get(i);
358 ASSERT_EQ(static_cast<int>(arraysize(sample_frames[i])),
359 proto_sample.entry().size());
360 ASSERT_TRUE(proto_sample.has_count());
361 EXPECT_EQ(i == 2 ? 2u : 1u, proto_sample.count());
362 for (size_t j = 0; j < arraysize(sample_frames[i]); j++) {
363 SCOPED_TRACE("frame " + base::IntToString(j));
364 const metrics::CallStackEntry& entry = proto_sample.entry().Get(j);
365 ASSERT_TRUE(entry.has_address());
366 const char *instruction_pointer = reinterpret_cast<const char *>(
367 sample_frames[i][j].instruction_pointer);
368 const char *module_base_address = reinterpret_cast<const char *>(
369 modules[sample_frames[i][j].module_index].base_address);
370 EXPECT_EQ(static_cast<uint64>(instruction_pointer - module_base_address),
371 entry.address());
372 ASSERT_TRUE(entry.has_module_id_index());
373 EXPECT_EQ(sample_frames[i][j].module_index, entry.module_id_index());
374 }
375 }
376 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698