/*
 * 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;
  }

 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_
