cpp/profiler/unwindc/runtime.h (155 lines of code) (raw):

/** * Copyright 2004-present, Facebook, Inc. * * 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. */ #pragma once #include <dlfcn.h> #include <fb/log.h> #include <fbjni/fbjni.h> #include <pthread.h> #include <stdint.h> #include <cstddef> namespace facebook { namespace profilo { namespace profiler { namespace ANDROID_NAMESPACE { // ANDROID_NAMESPACE is a preprocessor variable namespace fbjni = facebook::jni; struct string_t { const char* data; size_t length; }; typedef bool (*unwind_callback_t)(uintptr_t, void*); #if ANDROID_VERSION_NUM <= 601 pthread_key_t determineThreadInstanceTLSKey() { auto jlThread_class = fbjni::findClassLocal("java/lang/Thread"); auto jlThread_nativePeer = jlThread_class->getField<jlong>("nativePeer"); auto jlThread_currentThread = jlThread_class->getStaticMethod<jobject()>( "currentThread", "()Ljava/lang/Thread;"); auto jlThread = jlThread_currentThread(jlThread_class); auto nativePeer = jlThread->getFieldValue(jlThread_nativePeer); void* nativeThread = reinterpret_cast<void*>(nativePeer); constexpr int32_t kMaxPthreadKey = 128; constexpr int32_t kUserPthreadKeyStart = 0; #if ANDROID_VERSION_NUM < 600 constexpr int32_t kKeyValidFlag = 0; #else constexpr int32_t kKeyValidFlag = 1 << 31; // bionic tags keys by setting the MSB starting in Android 6.0 #endif for (pthread_key_t i = kUserPthreadKeyStart; i < kMaxPthreadKey; i++) { pthread_key_t tagged = i | kKeyValidFlag; if (pthread_getspecific(tagged) == nativeThread) { return tagged; } } throw std::runtime_error("Cannot determine thread instance TLS key"); } pthread_key_t getThreadInstanceTLSKey() { static pthread_key_t key = determineThreadInstanceTLSKey(); return key; } #elif ANDROID_VERSION_NUM >= 700 #if defined(__aarch64__) #define __get_tls() \ ({ \ void** __val; \ __asm__("mrs %0, tpidr_el0" : "=r"(__val)); \ __val; \ }) #elif defined(__arm__) #define __get_tls() \ ({ \ void** __val; \ __asm__("mrc p15, 0, %0, c13, c0, 3" : "=r"(__val)); \ __val; \ }) #elif defined(__mips__) #define __get_tls() \ /* On mips32r1, this goes via a kernel illegal instruction trap that's \ * optimized for v1. */ \ ({ \ register void** __val asm("v1"); \ __asm__( \ ".set push\n" \ ".set mips32r2\n" \ "rdhwr %0,$29\n" \ ".set pop\n" \ : "=r"(__val)); \ __val; \ }) #elif defined(__i386__) #define __get_tls() \ ({ \ void** __val; \ __asm__("movl %%gs:0, %0" : "=r"(__val)); \ __val; \ }) #elif defined(__x86_64__) #define __get_tls() \ ({ \ void** __val; \ __asm__("mov %%fs:0, %0" : "=r"(__val)); \ __val; \ }) #else #error unsupported architecture #endif #endif void* getThreadInstance() { #if ANDROID_VERSION_NUM >= 700 constexpr int kTlsSlotArtThreadSelf = 7; return __get_tls()[kTlsSlotArtThreadSelf]; #else return pthread_getspecific(getThreadInstanceTLSKey()); #endif } uintptr_t get_art_thread() { return reinterpret_cast<uintptr_t>(getThreadInstance()); } __attribute__((always_inline)) inline uint32_t CountShortyRefs( string_t shorty) { uint32_t result = 0; for (size_t i = 1; i < shorty.length; i++) { if (shorty.data[i] == 'L') { result++; } } return result; } __attribute__((always_inline)) inline string_t String(string_t data) { return data; } __attribute__((always_inline)) inline string_t String(uintptr_t ptr, const char*, const char*, uint32_t length) { return string_t{.data = (const char*)ptr, .length = length}; } __attribute__((always_inline)) inline uint64_t GetMethodTraceId( uint32_t dex_id, uint32_t method_id) { return (static_cast<uint64_t>(method_id) << 32) | dex_id; } __attribute__((always_inline)) inline uint8_t Read1(uintptr_t addr) { return *reinterpret_cast<uint8_t*>(addr); } __attribute__((always_inline)) inline uint16_t Read2(uintptr_t addr) { return *reinterpret_cast<uint16_t*>(addr); } __attribute__((always_inline)) inline uint32_t Read4(uintptr_t addr) { return *reinterpret_cast<uint32_t*>(addr); } __attribute__((always_inline)) inline uint64_t Read8(uintptr_t addr) { return *reinterpret_cast<uint64_t*>(addr); } __attribute__((always_inline)) inline uintptr_t AccessField( uintptr_t addr, uint32_t offset) { return addr + offset; } __attribute__((always_inline)) inline uintptr_t AccessArrayItem(uintptr_t addr, uint32_t item_size, uint32_t item) { return addr + (item_size * item); } __attribute__((always_inline)) inline uintptr_t AdvancePointer( uintptr_t ptr, uint32_t offset) { return ptr + offset; } } // namespace ANDROID_NAMESPACE } // namespace profiler } // namespace profilo } // namespace facebook