blob: d02638acc8804c5e39deaf66a3214eade3b4e236 [file]
/*
* 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/file.h"
#include "android-base/stringprintf.h"
#include "art_method-inl.h"
#include "com_android_art_rw_flags.h"
#include "compiler_callbacks.h"
#include "dex/descriptors_names.h"
#include "oat/oat_quick_method_header.h"
using ::android::base::GetBoolProperty;
using android::base::StringPrintf;
namespace art HIDDEN {
inline bool ShouldEnableProfileCode() {
if (Runtime::Current() != nullptr && Runtime::Current()->IsAotCompiler()) {
// For dex2oat invocations just look at the flag passed to the dex2oat command.
return Runtime::Current()->GetCompilerCallbacks()->ShouldEnableProfileCode();
}
bool build_enabled = GetBoolProperty("dalvik.vm.allow_profile_code", false);
return com::android::art::rw::flags::enable_profile_code_rw() && build_enabled;
}
static constexpr double kSecondsToNanoseconds = 1000 * 1000 * 1000;
inline 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__)
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_ = kSecondsToNanoseconds / 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;
}
static uint64_t GetFrequency() { return kSecondsToNanoseconds / tsc_to_nanosec_scaling_factor_; }
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 scaling_factor = (kSecondsToNanoseconds * 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_