Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/tools/profiler/tests/gtest/GeckoProfiler.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
 * License, v. 2.0. If a copy of the MPL was not distributed with this
5
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
// This file tests a lot of the profiler_*() functions in GeckoProfiler.h.
8
// Most of the tests just check that nothing untoward (e.g. crashes, deadlocks)
9
// happens when calling these functions. They don't do much inspection of
10
// profiler internals.
11
12
#include "gtest/gtest.h"
13
14
#include "GeckoProfiler.h"
15
#include "ProfilerMarkerPayload.h"
16
#include "jsapi.h"
17
#include "js/Initialization.h"
18
#include "mozilla/UniquePtrExtensions.h"
19
#include "ProfileBuffer.h"
20
#include "ProfileJSONWriter.h"
21
#include "nsIThread.h"
22
#include "nsThreadUtils.h"
23
24
#include <string.h>
25
26
// Note: profiler_init() has already been called in XRE_main(), so we can't
27
// test it here. Likewise for profiler_shutdown(), and AutoProfilerInit
28
// (which is just an RAII wrapper for profiler_init() and profiler_shutdown()).
29
30
using namespace mozilla;
31
32
typedef Vector<const char*> StrVec;
33
34
static void
35
InactiveFeaturesAndParamsCheck()
36
0
{
37
0
  int entries;
38
0
  double interval;
39
0
  uint32_t features;
40
0
  StrVec filters;
41
0
42
0
  ASSERT_TRUE(!profiler_is_active());
43
0
  ASSERT_TRUE(!profiler_feature_active(ProfilerFeature::MainThreadIO));
44
0
  ASSERT_TRUE(!profiler_feature_active(ProfilerFeature::Privacy));
45
0
46
0
  profiler_get_start_params(&entries, &interval, &features, &filters);
47
0
48
0
  ASSERT_TRUE(entries == 0);
49
0
  ASSERT_TRUE(interval == 0);
50
0
  ASSERT_TRUE(features == 0);
51
0
  ASSERT_TRUE(filters.empty());
52
0
}
53
54
static void
55
ActiveParamsCheck(int aEntries, double aInterval, uint32_t aFeatures,
56
                  const char** aFilters, size_t aFiltersLen)
57
0
{
58
0
  int entries;
59
0
  double interval;
60
0
  uint32_t features;
61
0
  StrVec filters;
62
0
63
0
  profiler_get_start_params(&entries, &interval, &features, &filters);
64
0
65
0
  ASSERT_TRUE(entries == aEntries);
66
0
  ASSERT_TRUE(interval == aInterval);
67
0
  ASSERT_TRUE(features == aFeatures);
68
0
  ASSERT_TRUE(filters.length() == aFiltersLen);
69
0
  for (size_t i = 0; i < aFiltersLen; i++) {
70
0
    ASSERT_TRUE(strcmp(filters[i], aFilters[i]) == 0);
71
0
  }
72
0
}
73
74
TEST(GeckoProfiler, FeaturesAndParams)
75
0
{
76
0
  InactiveFeaturesAndParamsCheck();
77
0
78
0
  // Try a couple of features and filters.
79
0
  {
80
0
    uint32_t features = ProfilerFeature::JS | ProfilerFeature::Threads;
81
0
    const char* filters[] = { "GeckoMain", "Compositor" };
82
0
83
0
    profiler_start(PROFILER_DEFAULT_ENTRIES, PROFILER_DEFAULT_INTERVAL,
84
0
                   features, filters, MOZ_ARRAY_LENGTH(filters));
85
0
86
0
    ASSERT_TRUE(profiler_is_active());
87
0
    ASSERT_TRUE(!profiler_feature_active(ProfilerFeature::MainThreadIO));
88
0
    ASSERT_TRUE(!profiler_feature_active(ProfilerFeature::Privacy));
89
0
90
0
    ActiveParamsCheck(PROFILER_DEFAULT_ENTRIES, PROFILER_DEFAULT_INTERVAL,
91
0
                      features, filters, MOZ_ARRAY_LENGTH(filters));
92
0
93
0
    profiler_stop();
94
0
95
0
    InactiveFeaturesAndParamsCheck();
96
0
  }
97
0
98
0
  // Try some different features and filters.
99
0
  {
100
0
    uint32_t features = ProfilerFeature::MainThreadIO |
101
0
                        ProfilerFeature::Privacy;
102
0
    const char* filters[] = { "GeckoMain", "Foo", "Bar" };
103
0
104
0
    profiler_start(999999, 3,
105
0
                   features, filters, MOZ_ARRAY_LENGTH(filters));
106
0
107
0
    ASSERT_TRUE(profiler_is_active());
108
0
    ASSERT_TRUE(profiler_feature_active(ProfilerFeature::MainThreadIO));
109
0
    ASSERT_TRUE(profiler_feature_active(ProfilerFeature::Privacy));
110
0
111
0
    // Profiler::Threads is added because filters has multiple entries.
112
0
    ActiveParamsCheck(999999, 3,
113
0
                      features | ProfilerFeature::Threads,
114
0
                      filters, MOZ_ARRAY_LENGTH(filters));
115
0
116
0
    profiler_stop();
117
0
118
0
    InactiveFeaturesAndParamsCheck();
119
0
  }
120
0
121
0
  // Try all supported features, and filters that match all threads.
122
0
  {
123
0
    uint32_t availableFeatures = profiler_get_available_features();
124
0
    const char* filters[] = { "" };
125
0
126
0
    profiler_start(88888, 10,
127
0
                   availableFeatures, filters, MOZ_ARRAY_LENGTH(filters));
128
0
129
0
    ASSERT_TRUE(profiler_is_active());
130
0
    ASSERT_TRUE(profiler_feature_active(ProfilerFeature::MainThreadIO));
131
0
    ASSERT_TRUE(profiler_feature_active(ProfilerFeature::Privacy));
132
0
133
0
    ActiveParamsCheck(88888, 10,
134
0
                      availableFeatures, filters, MOZ_ARRAY_LENGTH(filters));
135
0
136
0
    // Don't call profiler_stop() here.
137
0
  }
138
0
139
0
  // Try no features, and filters that match no threads.
140
0
  {
141
0
    uint32_t features = 0;
142
0
    const char* filters[] = { "NoThreadWillMatchThis" };
143
0
144
0
    // Second profiler_start() call in a row without an intervening
145
0
    // profiler_stop(); this will do an implicit profiler_stop() and restart.
146
0
    profiler_start(0, 0,
147
0
                   features, filters, MOZ_ARRAY_LENGTH(filters));
148
0
149
0
    ASSERT_TRUE(profiler_is_active());
150
0
    ASSERT_TRUE(!profiler_feature_active(ProfilerFeature::MainThreadIO));
151
0
    ASSERT_TRUE(!profiler_feature_active(ProfilerFeature::Privacy));
152
0
153
0
    // Entries and intervals go to defaults if 0 is specified.
154
0
    ActiveParamsCheck(PROFILER_DEFAULT_ENTRIES, PROFILER_DEFAULT_INTERVAL,
155
0
                      features | ProfilerFeature::Threads,
156
0
                      filters, MOZ_ARRAY_LENGTH(filters));
157
0
158
0
    profiler_stop();
159
0
160
0
    InactiveFeaturesAndParamsCheck();
161
0
162
0
    // These calls are no-ops.
163
0
    profiler_stop();
164
0
    profiler_stop();
165
0
166
0
    InactiveFeaturesAndParamsCheck();
167
0
  }
168
0
}
169
170
TEST(GeckoProfiler, EnsureStarted)
171
0
{
172
0
  InactiveFeaturesAndParamsCheck();
173
0
174
0
  uint32_t features = ProfilerFeature::JS | ProfilerFeature::Threads;
175
0
  const char* filters[] = { "GeckoMain", "Compositor" };
176
0
  {
177
0
    // Inactive -> Active
178
0
    profiler_ensure_started(PROFILER_DEFAULT_ENTRIES, PROFILER_DEFAULT_INTERVAL,
179
0
                            features, filters, MOZ_ARRAY_LENGTH(filters));
180
0
181
0
    ActiveParamsCheck(PROFILER_DEFAULT_ENTRIES, PROFILER_DEFAULT_INTERVAL,
182
0
                      features, filters, MOZ_ARRAY_LENGTH(filters));
183
0
  }
184
0
185
0
  {
186
0
    // Active -> Active with same settings
187
0
188
0
    // First, write some samples into the buffer.
189
0
    PR_Sleep(PR_MillisecondsToInterval(500));
190
0
191
0
    Maybe<ProfilerBufferInfo> info1 = profiler_get_buffer_info();
192
0
    ASSERT_TRUE(info1->mRangeEnd > 0);
193
0
194
0
    // Call profiler_ensure_started with the same settings as before.
195
0
    // This operation must not clear our buffer!
196
0
    profiler_ensure_started(PROFILER_DEFAULT_ENTRIES, PROFILER_DEFAULT_INTERVAL,
197
0
                            features, filters, MOZ_ARRAY_LENGTH(filters));
198
0
199
0
    ActiveParamsCheck(PROFILER_DEFAULT_ENTRIES, PROFILER_DEFAULT_INTERVAL,
200
0
                      features, filters, MOZ_ARRAY_LENGTH(filters));
201
0
202
0
    // Check that our position in the buffer stayed the same or advanced.
203
0
    // In particular, it shouldn't have reverted to the start.
204
0
    Maybe<ProfilerBufferInfo> info2 = profiler_get_buffer_info();
205
0
    ASSERT_TRUE(info2->mRangeEnd >= info1->mRangeEnd);
206
0
  }
207
0
208
0
  {
209
0
    // Active -> Active with *different* settings
210
0
211
0
    Maybe<ProfilerBufferInfo> info1 = profiler_get_buffer_info();
212
0
213
0
    // Call profiler_ensure_started with a different feature set than the one it's
214
0
    // currently running with. This is supposed to stop and restart the
215
0
    // profiler, thereby discarding the buffer contents.
216
0
    uint32_t differentFeatures = features | ProfilerFeature::Leaf;
217
0
    profiler_ensure_started(PROFILER_DEFAULT_ENTRIES, PROFILER_DEFAULT_INTERVAL,
218
0
                            differentFeatures,
219
0
                            filters, MOZ_ARRAY_LENGTH(filters));
220
0
221
0
    ActiveParamsCheck(PROFILER_DEFAULT_ENTRIES, PROFILER_DEFAULT_INTERVAL,
222
0
                      differentFeatures, filters, MOZ_ARRAY_LENGTH(filters));
223
0
224
0
    Maybe<ProfilerBufferInfo> info2 = profiler_get_buffer_info();
225
0
    ASSERT_TRUE(info2->mRangeEnd < info1->mRangeEnd);
226
0
  }
227
0
228
0
  {
229
0
    // Active -> Inactive
230
0
231
0
    profiler_stop();
232
0
233
0
    InactiveFeaturesAndParamsCheck();
234
0
  }
235
0
}
236
237
TEST(GeckoProfiler, DifferentThreads)
238
0
{
239
0
  InactiveFeaturesAndParamsCheck();
240
0
241
0
  nsCOMPtr<nsIThread> thread;
242
0
  nsresult rv = NS_NewNamedThread("GeckoProfGTest", getter_AddRefs(thread));
243
0
  ASSERT_TRUE(NS_SUCCEEDED(rv));
244
0
245
0
  // Control the profiler on a background thread and verify flags on the
246
0
  // main thread.
247
0
  {
248
0
    uint32_t features = ProfilerFeature::JS | ProfilerFeature::Threads;
249
0
    const char* filters[] = { "GeckoMain", "Compositor" };
250
0
251
0
    thread->Dispatch(
252
0
      NS_NewRunnableFunction("GeckoProfiler_DifferentThreads_Test::TestBody",
253
0
                             [&]() {
254
0
                               profiler_start(PROFILER_DEFAULT_ENTRIES,
255
0
                                              PROFILER_DEFAULT_INTERVAL,
256
0
                                              features,
257
0
                                              filters,
258
0
                                              MOZ_ARRAY_LENGTH(filters));
259
0
                             }),
260
0
      NS_DISPATCH_SYNC);
261
0
262
0
    ASSERT_TRUE(profiler_is_active());
263
0
    ASSERT_TRUE(!profiler_feature_active(ProfilerFeature::MainThreadIO));
264
0
    ASSERT_TRUE(!profiler_feature_active(ProfilerFeature::Privacy));
265
0
266
0
    ActiveParamsCheck(PROFILER_DEFAULT_ENTRIES, PROFILER_DEFAULT_INTERVAL,
267
0
                      features, filters, MOZ_ARRAY_LENGTH(filters));
268
0
269
0
    thread->Dispatch(
270
0
      NS_NewRunnableFunction("GeckoProfiler_DifferentThreads_Test::TestBody",
271
0
                             [&]() { profiler_stop(); }),
272
0
      NS_DISPATCH_SYNC);
273
0
274
0
    InactiveFeaturesAndParamsCheck();
275
0
  }
276
0
277
0
  // Control the profiler on the main thread and verify flags on a
278
0
  // background thread.
279
0
  {
280
0
    uint32_t features = ProfilerFeature::JS | ProfilerFeature::Threads;
281
0
    const char* filters[] = { "GeckoMain", "Compositor" };
282
0
283
0
    profiler_start(PROFILER_DEFAULT_ENTRIES, PROFILER_DEFAULT_INTERVAL,
284
0
                  features, filters, MOZ_ARRAY_LENGTH(filters));
285
0
286
0
    thread->Dispatch(
287
0
      NS_NewRunnableFunction(
288
0
        "GeckoProfiler_DifferentThreads_Test::TestBody",
289
0
        [&]() {
290
0
          ASSERT_TRUE(profiler_is_active());
291
0
          ASSERT_TRUE(!profiler_feature_active(ProfilerFeature::MainThreadIO));
292
0
          ASSERT_TRUE(!profiler_feature_active(ProfilerFeature::Privacy));
293
0
294
0
          ActiveParamsCheck(PROFILER_DEFAULT_ENTRIES,
295
0
                            PROFILER_DEFAULT_INTERVAL,
296
0
                            features,
297
0
                            filters,
298
0
                            MOZ_ARRAY_LENGTH(filters));
299
0
        }),
300
0
      NS_DISPATCH_SYNC);
301
0
302
0
    profiler_stop();
303
0
304
0
    thread->Dispatch(
305
0
      NS_NewRunnableFunction("GeckoProfiler_DifferentThreads_Test::TestBody",
306
0
                             [&]() { InactiveFeaturesAndParamsCheck(); }),
307
0
      NS_DISPATCH_SYNC);
308
0
  }
309
0
310
0
  thread->Shutdown();
311
0
}
312
313
TEST(GeckoProfiler, GetBacktrace)
314
0
{
315
0
  ASSERT_TRUE(!profiler_get_backtrace());
316
0
317
0
  {
318
0
    uint32_t features = ProfilerFeature::StackWalk;
319
0
    const char* filters[] = { "GeckoMain" };
320
0
321
0
    profiler_start(PROFILER_DEFAULT_ENTRIES, PROFILER_DEFAULT_INTERVAL,
322
0
                   features, filters, MOZ_ARRAY_LENGTH(filters));
323
0
324
0
    // These will be destroyed while the profiler is active.
325
0
    static const int N = 100;
326
0
    {
327
0
      UniqueProfilerBacktrace u[N];
328
0
      for (int i = 0; i < N; i++) {
329
0
        u[i] = profiler_get_backtrace();
330
0
        ASSERT_TRUE(u[i]);
331
0
      }
332
0
    }
333
0
334
0
    // These will be destroyed after the profiler stops.
335
0
    UniqueProfilerBacktrace u[N];
336
0
    for (int i = 0; i < N; i++) {
337
0
      u[i] = profiler_get_backtrace();
338
0
      ASSERT_TRUE(u[i]);
339
0
    }
340
0
341
0
    profiler_stop();
342
0
  }
343
0
344
0
  {
345
0
    uint32_t features = ProfilerFeature::Privacy;
346
0
    const char* filters[] = { "GeckoMain" };
347
0
348
0
    profiler_start(PROFILER_DEFAULT_ENTRIES, PROFILER_DEFAULT_INTERVAL,
349
0
                   features, filters, MOZ_ARRAY_LENGTH(filters));
350
0
351
0
    // No backtraces obtained when ProfilerFeature::Privacy is set.
352
0
    ASSERT_TRUE(!profiler_get_backtrace());
353
0
354
0
    profiler_stop();
355
0
  }
356
0
357
0
  ASSERT_TRUE(!profiler_get_backtrace());
358
0
}
359
360
TEST(GeckoProfiler, Pause)
361
0
{
362
0
  uint32_t features = ProfilerFeature::StackWalk;
363
0
  const char* filters[] = { "GeckoMain" };
364
0
365
0
  ASSERT_TRUE(!profiler_is_paused());
366
0
367
0
  profiler_start(PROFILER_DEFAULT_ENTRIES, PROFILER_DEFAULT_INTERVAL,
368
0
                 features, filters, MOZ_ARRAY_LENGTH(filters));
369
0
370
0
  ASSERT_TRUE(!profiler_is_paused());
371
0
372
0
  // Check that we are writing samples while not paused.
373
0
  Maybe<ProfilerBufferInfo> info1 = profiler_get_buffer_info();
374
0
  PR_Sleep(PR_MillisecondsToInterval(500));
375
0
  Maybe<ProfilerBufferInfo> info2 = profiler_get_buffer_info();
376
0
  ASSERT_TRUE(info1->mRangeEnd != info2->mRangeEnd);
377
0
378
0
  profiler_pause();
379
0
380
0
  ASSERT_TRUE(profiler_is_paused());
381
0
382
0
  // Check that we are not writing samples while paused.
383
0
  info1 = profiler_get_buffer_info();
384
0
  PR_Sleep(PR_MillisecondsToInterval(500));
385
0
  info2 = profiler_get_buffer_info();
386
0
  ASSERT_TRUE(info1->mRangeEnd == info2->mRangeEnd);
387
0
388
0
  profiler_resume();
389
0
390
0
  ASSERT_TRUE(!profiler_is_paused());
391
0
392
0
  profiler_stop();
393
0
394
0
  ASSERT_TRUE(!profiler_is_paused());
395
0
}
396
397
// A class that keeps track of how many instances have been created, streamed,
398
// and destroyed.
399
class GTestMarkerPayload : public ProfilerMarkerPayload
400
{
401
public:
402
  explicit GTestMarkerPayload(int aN)
403
    : mN(aN)
404
0
  {
405
0
    sNumCreated++;
406
0
  }
407
408
0
  virtual ~GTestMarkerPayload() { sNumDestroyed++; }
409
410
  virtual void StreamPayload(SpliceableJSONWriter& aWriter,
411
                             const mozilla::TimeStamp& aStartTime,
412
                             UniqueStacks& aUniqueStacks) override
413
0
  {
414
0
    StreamCommonProps("gtest", aWriter, aStartTime, aUniqueStacks);
415
0
    char buf[64];
416
0
    SprintfLiteral(buf, "gtest-%d", mN);
417
0
    aWriter.IntProperty(buf, mN);
418
0
    sNumStreamed++;
419
0
  }
420
421
private:
422
  int mN;
423
424
public:
425
  // The number of GTestMarkerPayload instances that have been created,
426
  // streamed, and destroyed.
427
  static int sNumCreated;
428
  static int sNumStreamed;
429
  static int sNumDestroyed;
430
};
431
432
int GTestMarkerPayload::sNumCreated = 0;
433
int GTestMarkerPayload::sNumStreamed = 0;
434
int GTestMarkerPayload::sNumDestroyed = 0;
435
436
TEST(GeckoProfiler, Markers)
437
0
{
438
0
  uint32_t features = ProfilerFeature::StackWalk;
439
0
  const char* filters[] = { "GeckoMain" };
440
0
441
0
  profiler_start(PROFILER_DEFAULT_ENTRIES, PROFILER_DEFAULT_INTERVAL,
442
0
                 features, filters, MOZ_ARRAY_LENGTH(filters));
443
0
444
0
  profiler_tracing("A", "B", TRACING_EVENT);
445
0
  PROFILER_TRACING("A", "C", TRACING_INTERVAL_START);
446
0
  PROFILER_TRACING("A", "C", TRACING_INTERVAL_END);
447
0
448
0
  UniqueProfilerBacktrace bt = profiler_get_backtrace();
449
0
  profiler_tracing("B", "A", TRACING_EVENT, std::move(bt));
450
0
451
0
  {
452
0
    AUTO_PROFILER_TRACING("C", "A");
453
0
  }
454
0
455
0
  profiler_add_marker("M1");
456
0
  profiler_add_marker(
457
0
    "M2", MakeUnique<TracingMarkerPayload>("C", TRACING_EVENT));
458
0
  PROFILER_ADD_MARKER("M3");
459
0
  profiler_add_marker(
460
0
    "M4",
461
0
    MakeUnique<TracingMarkerPayload>("C", TRACING_EVENT,
462
0
                                     profiler_get_backtrace()));
463
0
464
0
  for (int i = 0; i < 10; i++) {
465
0
    profiler_add_marker("M5", MakeUnique<GTestMarkerPayload>(i));
466
0
  }
467
0
468
0
  // Create two strings: one that is the maximum allowed length, and one that
469
0
  // is one char longer.
470
0
  static const size_t kMax = ProfileBuffer::kMaxFrameKeyLength;
471
0
  UniquePtr<char[]> okstr1 = MakeUnique<char[]>(kMax);
472
0
  UniquePtr<char[]> okstr2 = MakeUnique<char[]>(kMax);
473
0
  UniquePtr<char[]> longstr = MakeUnique<char[]>(kMax + 1);
474
0
  for (size_t i = 0; i < kMax; i++) {
475
0
    okstr1[i] = 'a';
476
0
    okstr2[i] = 'b';
477
0
    longstr[i] = 'c';
478
0
  }
479
0
  okstr1[kMax - 1] = '\0';
480
0
  okstr2[kMax - 1] = '\0';
481
0
  longstr[kMax] = '\0';
482
0
  AUTO_PROFILER_LABEL_DYNAMIC_CSTR("", LAYOUT, okstr1.get());
483
0
  AUTO_PROFILER_LABEL_DYNAMIC_CSTR("okstr2", LAYOUT, okstr2.get());
484
0
  AUTO_PROFILER_LABEL_DYNAMIC_CSTR("", LAYOUT, longstr.get());
485
0
486
0
  // Sleep briefly to ensure a sample is taken and the pending markers are
487
0
  // processed.
488
0
  PR_Sleep(PR_MillisecondsToInterval(500));
489
0
490
0
  SpliceableChunkedJSONWriter w;
491
0
  ASSERT_TRUE(profiler_stream_json_for_this_process(w));
492
0
493
0
  UniquePtr<char[]> profile = w.WriteFunc()->CopyData();
494
0
495
0
  // The GTestMarkerPayloads should have been created and streamed, but not yet
496
0
  // destroyed.
497
0
  ASSERT_TRUE(GTestMarkerPayload::sNumCreated == 10);
498
0
  ASSERT_TRUE(GTestMarkerPayload::sNumStreamed == 10);
499
0
  ASSERT_TRUE(GTestMarkerPayload::sNumDestroyed == 0);
500
0
  for (int i = 0; i < 10; i++) {
501
0
    char buf[64];
502
0
    SprintfLiteral(buf, "\"gtest-%d\"", i);
503
0
    ASSERT_TRUE(strstr(profile.get(), buf));
504
0
  }
505
0
506
0
  // okstr1 should appear as is.
507
0
  ASSERT_TRUE(strstr(profile.get(), okstr1.get()));
508
0
509
0
  // okstr2 should appear, slightly truncated with "okstr2 " in front of it.
510
0
  // (Nb: this only checks the front part of the marker string.)
511
0
  ASSERT_TRUE(strstr(profile.get(), "okstr2 bbbbbbbbb"));
512
0
513
0
  // longstr should be replaced with "(too long)".
514
0
  ASSERT_TRUE(!strstr(profile.get(), longstr.get()));
515
0
  ASSERT_TRUE(strstr(profile.get(), "(too long)"));
516
0
517
0
  profiler_stop();
518
0
519
0
  // The GTestMarkerPayloads should have been destroyed.
520
0
  ASSERT_TRUE(GTestMarkerPayload::sNumDestroyed == 10);
521
0
522
0
  for (int i = 0; i < 10; i++) {
523
0
    profiler_add_marker("M5", MakeUnique<GTestMarkerPayload>(i));
524
0
  }
525
0
526
0
  profiler_start(PROFILER_DEFAULT_ENTRIES, PROFILER_DEFAULT_INTERVAL,
527
0
                 features, filters, MOZ_ARRAY_LENGTH(filters));
528
0
529
0
  ASSERT_TRUE(profiler_stream_json_for_this_process(w));
530
0
531
0
  profiler_stop();
532
0
533
0
  // The second set of GTestMarkerPayloads should not have been streamed.
534
0
  ASSERT_TRUE(GTestMarkerPayload::sNumCreated == 20);
535
0
  ASSERT_TRUE(GTestMarkerPayload::sNumStreamed == 10);
536
0
  ASSERT_TRUE(GTestMarkerPayload::sNumDestroyed == 20);
537
0
}
538
539
TEST(GeckoProfiler, Time)
540
0
{
541
0
  uint32_t features = ProfilerFeature::StackWalk;
542
0
  const char* filters[] = { "GeckoMain" };
543
0
544
0
  double t1 = profiler_time();
545
0
  double t2 = profiler_time();
546
0
  ASSERT_TRUE(t1 <= t2);
547
0
548
0
  // profiler_start() restarts the timer used by profiler_time().
549
0
  profiler_start(PROFILER_DEFAULT_ENTRIES, PROFILER_DEFAULT_INTERVAL,
550
0
                 features, filters, MOZ_ARRAY_LENGTH(filters));
551
0
552
0
  double t3 = profiler_time();
553
0
  double t4 = profiler_time();
554
0
  ASSERT_TRUE(t3 <= t4);
555
0
556
0
  profiler_stop();
557
0
558
0
  double t5 = profiler_time();
559
0
  double t6 = profiler_time();
560
0
  ASSERT_TRUE(t4 <= t5 && t1 <= t6);
561
0
}
562
563
TEST(GeckoProfiler, GetProfile)
564
0
{
565
0
  uint32_t features = ProfilerFeature::StackWalk;
566
0
  const char* filters[] = { "GeckoMain" };
567
0
568
0
  ASSERT_TRUE(!profiler_get_profile());
569
0
570
0
  profiler_start(PROFILER_DEFAULT_ENTRIES, PROFILER_DEFAULT_INTERVAL,
571
0
                 features, filters, MOZ_ARRAY_LENGTH(filters));
572
0
573
0
  UniquePtr<char[]> profile = profiler_get_profile();
574
0
  ASSERT_TRUE(profile && profile[0] == '{');
575
0
576
0
  profiler_stop();
577
0
578
0
  ASSERT_TRUE(!profiler_get_profile());
579
0
}
580
581
static void
582
JSONOutputCheck(const char* aOutput)
583
0
{
584
0
  // Check that various expected strings are in the JSON.
585
0
586
0
  ASSERT_TRUE(aOutput);
587
0
  ASSERT_TRUE(aOutput[0] == '{');
588
0
589
0
  ASSERT_TRUE(strstr(aOutput, "\"libs\""));
590
0
591
0
  ASSERT_TRUE(strstr(aOutput, "\"meta\""));
592
0
  ASSERT_TRUE(strstr(aOutput, "\"version\""));
593
0
  ASSERT_TRUE(strstr(aOutput, "\"startTime\""));
594
0
595
0
  ASSERT_TRUE(strstr(aOutput, "\"threads\""));
596
0
  ASSERT_TRUE(strstr(aOutput, "\"GeckoMain\""));
597
0
  ASSERT_TRUE(strstr(aOutput, "\"samples\""));
598
0
  ASSERT_TRUE(strstr(aOutput, "\"markers\""));
599
0
  ASSERT_TRUE(strstr(aOutput, "\"stackTable\""));
600
0
  ASSERT_TRUE(strstr(aOutput, "\"frameTable\""));
601
0
  ASSERT_TRUE(strstr(aOutput, "\"stringTable\""));
602
0
}
603
604
TEST(GeckoProfiler, StreamJSONForThisProcess)
605
0
{
606
0
  uint32_t features = ProfilerFeature::StackWalk;
607
0
  const char* filters[] = { "GeckoMain" };
608
0
609
0
  SpliceableChunkedJSONWriter w;
610
0
  ASSERT_TRUE(!profiler_stream_json_for_this_process(w));
611
0
612
0
  profiler_start(PROFILER_DEFAULT_ENTRIES, PROFILER_DEFAULT_INTERVAL,
613
0
                 features, filters, MOZ_ARRAY_LENGTH(filters));
614
0
615
0
  w.Start();
616
0
  ASSERT_TRUE(profiler_stream_json_for_this_process(w));
617
0
  w.End();
618
0
619
0
  UniquePtr<char[]> profile = w.WriteFunc()->CopyData();
620
0
621
0
  JSONOutputCheck(profile.get());
622
0
623
0
  profiler_stop();
624
0
625
0
  ASSERT_TRUE(!profiler_stream_json_for_this_process(w));
626
0
}
627
628
TEST(GeckoProfiler, StreamJSONForThisProcessThreaded)
629
0
{
630
0
  // Same as the previous test, but calling some things on background threads.
631
0
  nsCOMPtr<nsIThread> thread;
632
0
  nsresult rv = NS_NewNamedThread("GeckoProfGTest", getter_AddRefs(thread));
633
0
  ASSERT_TRUE(NS_SUCCEEDED(rv));
634
0
635
0
  uint32_t features = ProfilerFeature::StackWalk;
636
0
  const char* filters[] = { "GeckoMain" };
637
0
638
0
  SpliceableChunkedJSONWriter w;
639
0
  ASSERT_TRUE(!profiler_stream_json_for_this_process(w));
640
0
641
0
  // Start the profiler on the main thread.
642
0
  profiler_start(PROFILER_DEFAULT_ENTRIES, PROFILER_DEFAULT_INTERVAL,
643
0
                features, filters, MOZ_ARRAY_LENGTH(filters));
644
0
645
0
  // Call profiler_stream_json_for_this_process on a background thread.
646
0
  thread->Dispatch(
647
0
    NS_NewRunnableFunction(
648
0
      "GeckoProfiler_StreamJSONForThisProcessThreaded_Test::TestBody",
649
0
      [&]() {
650
0
        w.Start();
651
0
        ASSERT_TRUE(profiler_stream_json_for_this_process(w));
652
0
        w.End();
653
0
      }),
654
0
    NS_DISPATCH_SYNC);
655
0
656
0
  UniquePtr<char[]> profile = w.WriteFunc()->CopyData();
657
0
658
0
  JSONOutputCheck(profile.get());
659
0
660
0
  // Stop the profiler and call profiler_stream_json_for_this_process on a
661
0
  // background thread.
662
0
  thread->Dispatch(
663
0
    NS_NewRunnableFunction(
664
0
      "GeckoProfiler_StreamJSONForThisProcessThreaded_Test::TestBody",
665
0
      [&]() {
666
0
        profiler_stop();
667
0
        ASSERT_TRUE(!profiler_stream_json_for_this_process(w));
668
0
      }),
669
0
    NS_DISPATCH_SYNC);
670
0
  thread->Shutdown();
671
0
672
0
  // Call profiler_stream_json_for_this_process on the main thread.
673
0
  ASSERT_TRUE(!profiler_stream_json_for_this_process(w));
674
0
}
675
676
TEST(GeckoProfiler, ProfilingStack)
677
0
{
678
0
  uint32_t features = ProfilerFeature::StackWalk;
679
0
  const char* filters[] = { "GeckoMain" };
680
0
681
0
  AUTO_PROFILER_LABEL("A::B", OTHER);
682
0
683
0
  UniqueFreePtr<char> dynamic(strdup("dynamic"));
684
0
  {
685
0
    AUTO_PROFILER_LABEL_DYNAMIC_CSTR("A::C", JS, dynamic.get());
686
0
    AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING(
687
0
      "A::C2", JS, nsDependentCString(dynamic.get()));
688
0
    AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING(
689
0
      "A::C3", JS, NS_ConvertUTF8toUTF16(dynamic.get()));
690
0
691
0
    profiler_start(PROFILER_DEFAULT_ENTRIES, PROFILER_DEFAULT_INTERVAL,
692
0
                   features, filters, MOZ_ARRAY_LENGTH(filters));
693
0
694
0
    ASSERT_TRUE(profiler_get_backtrace());
695
0
  }
696
0
697
0
  AutoProfilerLabel label1("A", nullptr, 888,
698
0
                           js::ProfilingStackFrame::Category::DOM);
699
0
  AutoProfilerLabel label2("A", dynamic.get(), 888,
700
0
                           js::ProfilingStackFrame::Category::NETWORK);
701
0
  ASSERT_TRUE(profiler_get_backtrace());
702
0
703
0
  profiler_stop();
704
0
705
0
  ASSERT_TRUE(!profiler_get_profile());
706
0
}
707
708
TEST(GeckoProfiler, Bug1355807)
709
0
{
710
0
  uint32_t features = ProfilerFeature::JS;
711
0
  const char* manyThreadsFilter[] = { "" };
712
0
  const char* fewThreadsFilter[] = { "GeckoMain" };
713
0
714
0
  profiler_start(PROFILER_DEFAULT_ENTRIES, PROFILER_DEFAULT_INTERVAL,
715
0
                 features,
716
0
                 manyThreadsFilter, MOZ_ARRAY_LENGTH(manyThreadsFilter));
717
0
718
0
  profiler_start(PROFILER_DEFAULT_ENTRIES, PROFILER_DEFAULT_INTERVAL,
719
0
                 features,
720
0
                 fewThreadsFilter, MOZ_ARRAY_LENGTH(fewThreadsFilter));
721
0
722
0
  // In bug 1355807 this caused an assertion failure in StopJSSampling().
723
0
  profiler_start(PROFILER_DEFAULT_ENTRIES, PROFILER_DEFAULT_INTERVAL,
724
0
                 features,
725
0
                 fewThreadsFilter, MOZ_ARRAY_LENGTH(fewThreadsFilter));
726
0
727
0
  profiler_stop();
728
0
}
729
730
class GTestStackCollector final : public ProfilerStackCollector
731
{
732
public:
733
  GTestStackCollector()
734
    : mSetIsMainThread(0)
735
    , mFrames(0)
736
0
  {}
737
738
0
  virtual void SetIsMainThread() { mSetIsMainThread++; }
739
740
0
  virtual void CollectNativeLeafAddr(void* aAddr) { mFrames++; }
741
0
  virtual void CollectJitReturnAddr(void* aAddr) { mFrames++; }
742
0
  virtual void CollectWasmFrame(const char* aLabel) { mFrames++; }
743
0
  virtual void CollectProfilingStackFrame(const js::ProfilingStackFrame& aFrame) { mFrames++; }
744
745
  int mSetIsMainThread;
746
  int mFrames;
747
};
748
749
void DoSuspendAndSample(int aTid, nsIThread* aThread)
750
0
{
751
0
  aThread->Dispatch(
752
0
    NS_NewRunnableFunction(
753
0
      "GeckoProfiler_SuspendAndSample_Test::TestBody",
754
0
      [&]() {
755
0
        uint32_t features = ProfilerFeature::Leaf;
756
0
        GTestStackCollector collector;
757
0
        profiler_suspend_and_sample_thread(aTid, features, collector,
758
0
                                           /* sampleNative = */ true);
759
0
760
0
        ASSERT_TRUE(collector.mSetIsMainThread == 1);
761
0
        ASSERT_TRUE(collector.mFrames > 0);
762
0
      }),
763
0
    NS_DISPATCH_SYNC);
764
0
}
765
766
TEST(GeckoProfiler, SuspendAndSample)
767
0
{
768
0
  nsCOMPtr<nsIThread> thread;
769
0
  nsresult rv = NS_NewNamedThread("GeckoProfGTest", getter_AddRefs(thread));
770
0
  ASSERT_TRUE(NS_SUCCEEDED(rv));
771
0
772
0
  int tid = Thread::GetCurrentId();
773
0
774
0
  ASSERT_TRUE(!profiler_is_active());
775
0
776
0
  // Suspend and sample while the profiler is inactive.
777
0
  DoSuspendAndSample(tid, thread);
778
0
779
0
  uint32_t features = ProfilerFeature::JS | ProfilerFeature::Threads;
780
0
  const char* filters[] = { "GeckoMain", "Compositor" };
781
0
782
0
  profiler_start(PROFILER_DEFAULT_ENTRIES, PROFILER_DEFAULT_INTERVAL,
783
0
                 features, filters, MOZ_ARRAY_LENGTH(filters));
784
0
785
0
  ASSERT_TRUE(profiler_is_active());
786
0
787
0
  // Suspend and sample while the profiler is active.
788
0
  DoSuspendAndSample(tid, thread);
789
0
790
0
  profiler_stop();
791
0
792
0
  ASSERT_TRUE(!profiler_is_active());
793
0
}
794