summaryrefslogtreecommitdiff
path: root/runtime/trace_common.h
blob: f29b34746295d9cff8b13d06feb6fa323ef51ce8 (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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef ART_RUNTIME_TRACE_COMMON_H_
#define ART_RUNTIME_TRACE_COMMON_H_

#include "android-base/stringprintf.h"
#include "art_method-inl.h"
#include "dex/descriptors_names.h"
#include "oat/oat_quick_method_header.h"

using android::base::StringPrintf;

namespace art HIDDEN {

static std::string GetMethodInfoLine(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) {
  method = method->GetInterfaceMethodIfProxy(kRuntimePointerSize);
  return StringPrintf("%s\t%s\t%s\t%s\n",
                      PrettyDescriptor(method->GetDeclaringClassDescriptor()).c_str(),
                      method->GetName(),
                      method->GetSignature().ToString().c_str(),
                      method->GetDeclaringClassSourceFile());
}

class TimestampCounter {
 public:
  static uint64_t GetTimestamp() {
    uint64_t t = 0;
#if defined(__arm__)
    // On ARM 32 bit, we don't always have access to the timestamp counters from user space. There
    // is no easy way to check if it is safe to read the timestamp counters. There is HWCAP_EVTSTRM
    // which is set when generic timer is available but not necessarily from the user space. Kernel
    // disables access to generic timer when there are known problems on the target CPUs. Sometimes
    // access is disabled only for 32-bit processes even when 64-bit processes can accesses the
    // timer from user space. These are not reflected in the HWCAP_EVTSTRM capability.So just
    // fallback to clock_gettime on these processes. See b/289178149 for more discussion.
    t = NanoTime();
#elif defined(__aarch64__)
    // See Arm Architecture Registers  Armv8 section System Registers
    asm volatile("mrs %0, cntvct_el0" : "=r"(t));
#elif defined(__i386__) || defined(__x86_64__)
    // rdtsc returns two 32-bit values in rax and rdx even on 64-bit architectures.
    unsigned int lo, hi;
    asm volatile("rdtsc" : "=a"(lo), "=d"(hi));
    t = (static_cast<uint64_t>(hi) << 32) | lo;
#elif defined(__riscv)
    asm volatile("rdtime %0" : "=r"(t));
#else
    t = NanoTime();
#endif
    return t;
  }

  static void InitializeTimestampCounters() {
    // It is sufficient to initialize this once for the entire execution. Just return if it is
    // already initialized.
    if (tsc_to_nanosec_scaling_factor > 0.0) {
      return;
    }

#if defined(__arm__)
    // On ARM 32 bit, we don't always have access to the timestamp counters from
    // user space. Seem comment in GetTimestamp for more details.
    tsc_to_nanosec_scaling_factor = 1.0;
#elif defined(__aarch64__)
    double seconds_to_nanoseconds = 1000 * 1000;
    uint64_t freq = 0;
    // See Arm Architecture Registers  Armv8 section System Registers
    asm volatile("mrs %0,  cntfrq_el0" : "=r"(freq));
    if (freq == 0) {
      // It is expected that cntfrq_el0 is correctly setup during system initialization but some
      // devices don't do this. In such cases fall back to computing the frequency. See b/315139000.
      tsc_to_nanosec_scaling_factor = computeScalingFactor();
    } else {
      tsc_to_nanosec_scaling_factor = seconds_to_nanoseconds / static_cast<double>(freq);
    }
#elif defined(__i386__) || defined(__x86_64__)
    tsc_to_nanosec_scaling_factor = GetScalingFactorForX86();
#else
    tsc_to_nanosec_scaling_factor = 1.0;
#endif
  }

  static ALWAYS_INLINE uint64_t GetNanoTime(uint64_t counter) {
    DCHECK(tsc_to_nanosec_scaling_factor > 0.0) << tsc_to_nanosec_scaling_factor;
    return tsc_to_nanosec_scaling_factor * counter;
  }

 private:
#if defined(__i386__) || defined(__x86_64__) || defined(__aarch64__)
  // Here we compute the scaling factor by sleeping for a millisecond. Alternatively, we could
  // generate raw timestamp counter and also time using clock_gettime at the start and the end of
  // the trace. We can compute the frequency of timestamp counter upadtes in the post processing
  // step using these two samples. However, that would require a change in Android Studio which is
  // the main consumer of these profiles. For now, just compute the frequency of tsc updates here.
  static double computeScalingFactor() {
    uint64_t start = NanoTime();
    uint64_t start_tsc = GetTimestamp();
    // Sleep for one millisecond.
    usleep(1000);
    uint64_t diff_tsc = GetTimestamp() - start_tsc;
    uint64_t diff_time = NanoTime() - start;
    double scaling_factor = static_cast<double>(diff_time) / diff_tsc;
    DCHECK(scaling_factor > 0.0) << scaling_factor;
    return scaling_factor;
  }
#endif

#if defined(__i386__) || defined(__x86_64__)
  static double GetScalingFactorForX86() {
    uint32_t eax, ebx, ecx;
    asm volatile("cpuid" : "=a"(eax), "=b"(ebx), "=c"(ecx) : "a"(0x0), "c"(0));
    if (eax < 0x15) {
      // There is no 15H - Timestamp counter and core crystal clock information
      // leaf. Just compute the frequency.
      return computeScalingFactor();
    }

    // From Intel architecture-instruction-set-extensions-programming-reference:
    // EBX[31:0]/EAX[31:0] indicates the ratio of the TSC frequency and the
    // core crystal clock frequency.
    // If EBX[31:0] is 0, the TSC and "core crystal clock" ratio is not enumerated.
    // If ECX is 0, the nominal core crystal clock frequency is not enumerated.
    // "TSC frequency" = "core crystal clock frequency" * EBX/EAX.
    // The core crystal clock may differ from the reference clock, bus clock, or core clock
    // frequencies.
    // EAX Bits 31 - 00: An unsigned integer which is the denominator of the
    //                   TSC/"core crystal clock" ratio.
    // EBX Bits 31 - 00: An unsigned integer which is the numerator of the
    //                   TSC/"core crystal clock" ratio.
    // ECX Bits 31 - 00: An unsigned integer which is the nominal frequency of the core
    //                   crystal clock in Hz.
    // EDX Bits 31 - 00: Reserved = 0.
    asm volatile("cpuid" : "=a"(eax), "=b"(ebx), "=c"(ecx) : "a"(0x15), "c"(0));
    if (ebx == 0 || ecx == 0) {
      return computeScalingFactor();
    }
    double coreCrystalFreq = ecx;
    // frequency = coreCrystalFreq * (ebx / eax)
    // scaling_factor = seconds_to_nanoseconds / frequency
    //                = seconds_to_nanoseconds * eax / (coreCrystalFreq * ebx)
    double seconds_to_nanoseconds = 1000 * 1000;
    double scaling_factor = (seconds_to_nanoseconds * eax) / (coreCrystalFreq * ebx);
    return scaling_factor;
  }
#endif

  // Scaling factor to convert timestamp counter into wall clock time reported in nano seconds.
  // This is initialized at the start of tracing using the timestamp counter update frequency.
  // See InitializeTimestampCounters for more details.
  static double tsc_to_nanosec_scaling_factor;
};

}  // namespace art

#endif  // ART_RUNTIME_TRACE_COMMON_H_