| /* |
| * 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_ |