summaryrefslogtreecommitdiffstats
path: root/widget/android/ANRReporter.cpp
blob: 30d9b3d7624d468375703677110ae4e94d3bb7a5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
/* -*- Mode: c++; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "ANRReporter.h"
#include "GeckoProfiler.h"

#include <unistd.h>

namespace mozilla {

bool
ANRReporter::RequestNativeStack(bool aUnwind)
{
    if (profiler_is_active()) {
        // Don't proceed if profiler is already running
        return false;
    }
    // WARNING: we are on the ANR reporter thread at this point and it is
    // generally unsafe to use the profiler from off the main thread. However,
    // the risk here is limited because for most users, the profiler is not run
    // elsewhere. See the discussion in Bug 863777, comment 13
    const char *NATIVE_STACK_FEATURES[] =
        {"leaf", "threads", "privacy"};
    const char *NATIVE_STACK_UNWIND_FEATURES[] =
        {"leaf", "threads", "privacy", "stackwalk"};

    const char **features = NATIVE_STACK_FEATURES;
    size_t features_size = sizeof(NATIVE_STACK_FEATURES);
    if (aUnwind) {
        features = NATIVE_STACK_UNWIND_FEATURES;
        features_size = sizeof(NATIVE_STACK_UNWIND_FEATURES);
        // We want the new unwinder if the unwind mode has not been set yet
        putenv("MOZ_PROFILER_NEW=1");
    }

    const char *NATIVE_STACK_THREADS[] =
        {"GeckoMain", "Compositor"};
    // Buffer one sample and let the profiler wait a long time
    profiler_start(100, 10000, features, features_size / sizeof(char*),
        NATIVE_STACK_THREADS, sizeof(NATIVE_STACK_THREADS) / sizeof(char*));
    return true;
}

jni::String::LocalRef
ANRReporter::GetNativeStack()
{
    if (!profiler_is_active()) {
        // Maybe profiler support is disabled?
        return nullptr;
    }

    // Timeout if we don't get a profiler sample after 5 seconds.
    const PRIntervalTime timeout = PR_SecondsToInterval(5);
    const PRIntervalTime startTime = PR_IntervalNow();

    // Pointer to a profile JSON string
    typedef mozilla::UniquePtr<char[]> ProfilePtr;

    ProfilePtr profile(profiler_get_profile());

    while (profile && !strstr(profile.get(), "\"samples\":[{")) {
        // no sample yet?
        if (PR_IntervalNow() - startTime >= timeout) {
            return nullptr;
        }
        usleep(100000ul); // Sleep for 100ms
        profile = ProfilePtr(profiler_get_profile());
    }

    if (profile) {
        return jni::String::Param(profile.get());
    }
    return nullptr;
}

void
ANRReporter::ReleaseNativeStack()
{
    if (!profiler_is_active()) {
        // Maybe profiler support is disabled?
        return;
    }
    profiler_stop();
}

} // namespace