Add timestamps to JIT/DEX native debug info.

This a forward-looking change intended to allow simpleperf to
reliably correlate samples and native debug information.

I have added the timestamps to both JIT and DEX, and refactored
the code in the process to avoid code duplication.

Test: testrunner.py -t 137
Change-Id: I45fa4310305aff540e036db9af15a86c5b8b7aff
diff --git a/runtime/jit/debugger_interface.cc b/runtime/jit/debugger_interface.cc
index d60f70a..505e626 100644
--- a/runtime/jit/debugger_interface.cc
+++ b/runtime/jit/debugger_interface.cc
@@ -18,18 +18,35 @@
 
 #include <android-base/logging.h>
 
+#include "base/array_ref.h"
 #include "base/mutex.h"
+#include "base/time_utils.h"
 #include "thread-current-inl.h"
 #include "thread.h"
 
 #include <unordered_map>
 
-namespace art {
+//
+// Debug interface for native tools (gdb, lldb, libunwind, simpleperf).
+//
+// See http://sourceware.org/gdb/onlinedocs/gdb/Declarations.html
+//
+// There are two ways for native tools to access the debug data safely:
+//
+// 1) Synchronously, by setting a breakpoint in the __*_debug_register_code
+//    method, which is called after every modification of the linked list.
+//    GDB does this, but it is complex to set up and it stops the process.
+//
+// 2) Asynchronously, by monitoring the action_counter_, which is incremented
+//    on every modification of the linked list and kept at -1 during updates.
+//    Therefore, if the tool process reads the counter both before and after
+//    iterating over the linked list, and the counters match and are not -1,
+//    the tool process can be sure the list was not modified during the read.
+//    Obviously, it can also cache the data and use the counter to determine
+//    if the cache is up to date, or to intelligently update it if needed.
+//
 
-// -------------------------------------------------------------------
-// Binary GDB JIT Interface as described in
-//   http://sourceware.org/gdb/onlinedocs/gdb/Declarations.html
-// -------------------------------------------------------------------
+namespace art {
 extern "C" {
   typedef enum {
     JIT_NOACTION = 0,
@@ -40,168 +57,193 @@
   struct JITCodeEntry {
     JITCodeEntry* next_;
     JITCodeEntry* prev_;
-    const uint8_t *symfile_addr_;
-    uint64_t symfile_size_;
-    uint32_t ref_count;  // ART internal field.
+    const uint8_t* symfile_addr_;
+    uint64_t symfile_size_;  // Beware of the offset (12 on x86; but 16 on ARM32).
+
+    // Android-specific fields:
+    uint64_t register_timestamp_;  // CLOCK_MONOTONIC time of entry registration.
   };
 
   struct JITDescriptor {
-    uint32_t version_;
-    uint32_t action_flag_;
-    JITCodeEntry* relevant_entry_;
-    JITCodeEntry* first_entry_;
+    uint32_t version_ = 1;                    // NB: GDB supports only version 1.
+    uint32_t action_flag_ = JIT_NOACTION;     // One of the JITAction enum values.
+    JITCodeEntry* relevant_entry_ = nullptr;  // The entry affected by the action.
+    JITCodeEntry* first_entry_ = nullptr;     // Head of link list of all entries.
+
+    // Android-specific fields:
+    uint8_t magic_[8] = {'A', 'n', 'd', 'r', 'o', 'i', 'd', '1'};
+    uint32_t flags_ = 0;                   // Reserved for future use. Must be 0.
+    uint32_t sizeof_descriptor = sizeof(JITDescriptor);
+    uint32_t sizeof_entry = sizeof(JITCodeEntry);
+    std::atomic_int32_t action_counter_;   // Number of actions, or -1 if locked.
+                                           // It can overflow from INT32_MAX to 0.
+    uint64_t action_timestamp_ = 1;        // CLOCK_MONOTONIC time of last action.
   };
 
-  // GDB will place breakpoint into this function.
-  // To prevent GCC from inlining or removing it we place noinline attribute
-  // and inline assembler statement inside.
-  void __attribute__((noinline)) __jit_debug_register_code();
+  // Check that std::atomic_int32_t has the same layout as int32_t.
+  static_assert(alignof(std::atomic_int32_t) == alignof(int32_t), "Weird alignment");
+  static_assert(sizeof(std::atomic_int32_t) == sizeof(int32_t), "Weird size");
+
+  // GDB may set breakpoint here. We must ensure it is not removed or deduplicated.
   void __attribute__((noinline)) __jit_debug_register_code() {
     __asm__("");
   }
 
-  // Call __jit_debug_register_code indirectly via global variable.
-  // This gives the debugger an easy way to inject custom code to handle the events.
+  // Alternatively, native tools may overwrite this field to execute custom handler.
   void (*__jit_debug_register_code_ptr)() = __jit_debug_register_code;
 
-  // GDB will inspect contents of this descriptor.
-  // Static initialization is necessary to prevent GDB from seeing
-  // uninitialized descriptor.
-  JITDescriptor __jit_debug_descriptor = { 1, JIT_NOACTION, nullptr, nullptr };
+  // The root data structure describing of all JITed methods.
+  JITDescriptor __jit_debug_descriptor {};
 
-  // Incremented whenever __jit_debug_descriptor is modified.
-  uint32_t __jit_debug_descriptor_timestamp = 0;
-
-  struct DEXFileEntry {
-    DEXFileEntry* next_;
-    DEXFileEntry* prev_;
-    const void* dexfile_;
-  };
-
-  DEXFileEntry* __art_debug_dexfiles = nullptr;
-
-  // Incremented whenever __art_debug_dexfiles is modified.
-  uint32_t __art_debug_dexfiles_timestamp = 0;
-}
-
-static size_t g_jit_debug_mem_usage
-    GUARDED_BY(Locks::native_debug_interface_lock_) = 0;
-
-static std::unordered_map<const void*, DEXFileEntry*> g_dexfile_entries
-    GUARDED_BY(Locks::native_debug_interface_lock_);
-
-void RegisterDexFileForNative(Thread* current_thread, const void* dexfile_header) {
-  MutexLock mu(current_thread, *Locks::native_debug_interface_lock_);
-  if (g_dexfile_entries.count(dexfile_header) == 0) {
-    DEXFileEntry* entry = new DEXFileEntry();
-    CHECK(entry != nullptr);
-    entry->dexfile_ = dexfile_header;
-    entry->prev_ = nullptr;
-    entry->next_ = __art_debug_dexfiles;
-    if (entry->next_ != nullptr) {
-      entry->next_->prev_ = entry;
-    }
-    __art_debug_dexfiles = entry;
-    __art_debug_dexfiles_timestamp++;
-    g_dexfile_entries.emplace(dexfile_header, entry);
+  // The following globals mirror the ones above, but are used to register dex files.
+  void __attribute__((noinline)) __dex_debug_register_code() {
+    __asm__("");
   }
+  void (*__dex_debug_register_code_ptr)() = __dex_debug_register_code;
+  JITDescriptor __dex_debug_descriptor {};
 }
 
-void DeregisterDexFileForNative(Thread* current_thread, const void* dexfile_header) {
-  MutexLock mu(current_thread, *Locks::native_debug_interface_lock_);
-  auto it = g_dexfile_entries.find(dexfile_header);
-  // We register dex files in the class linker and free them in DexFile_closeDexFile,
-  // but might be cases where we load the dex file without using it in the class linker.
-  if (it != g_dexfile_entries.end()) {
-    DEXFileEntry* entry = it->second;
-    if (entry->prev_ != nullptr) {
-      entry->prev_->next_ = entry->next_;
-    } else {
-      __art_debug_dexfiles = entry->next_;
-    }
-    if (entry->next_ != nullptr) {
-      entry->next_->prev_ = entry->prev_;
-    }
-    __art_debug_dexfiles_timestamp++;
-    delete entry;
-    g_dexfile_entries.erase(it);
-  }
+// Mark the descriptor as "locked", so native tools know the data is unstable.
+// Returns the old value of the counter.
+static int32_t LockActionCounter(JITDescriptor& descriptor) {
+  return descriptor.action_counter_.exchange(-1);
 }
 
-JITCodeEntry* CreateJITCodeEntry(const std::vector<uint8_t>& symfile) {
-  DCHECK_NE(symfile.size(), 0u);
+// Mark the descriptor as "unlocked", so native tools know the data is safe to read.
+// It will also increment the value so that the tools know the data has changed.
+static void UnlockActionCounter(JITDescriptor& descriptor, int32_t old_value) {
+  int32_t new_value = (old_value + 1) & 0x7FFFFFFF;  // Handle overflow to avoid -1.
+  descriptor.action_counter_.store(new_value);
+}
 
-  // Make a copy of the buffer. We want to shrink it anyway.
-  uint8_t* symfile_copy = new uint8_t[symfile.size()];
-  CHECK(symfile_copy != nullptr);
-  memcpy(symfile_copy, symfile.data(), symfile.size());
+static JITCodeEntry* CreateJITCodeEntryInternal(
+    JITDescriptor& descriptor,
+    void (*register_code_ptr)(),
+    const ArrayRef<const uint8_t>& symfile)
+    REQUIRES(Locks::native_debug_interface_lock_) {
+  int32_t old_action_counter = LockActionCounter(descriptor);
 
   JITCodeEntry* entry = new JITCodeEntry;
   CHECK(entry != nullptr);
-  entry->symfile_addr_ = symfile_copy;
+  entry->symfile_addr_ = symfile.data();
   entry->symfile_size_ = symfile.size();
   entry->prev_ = nullptr;
-  entry->ref_count = 0;
-  entry->next_ = __jit_debug_descriptor.first_entry_;
+  entry->next_ = descriptor.first_entry_;
+  entry->register_timestamp_ = NanoTime();
   if (entry->next_ != nullptr) {
     entry->next_->prev_ = entry;
   }
-  g_jit_debug_mem_usage += sizeof(JITCodeEntry) + entry->symfile_size_;
-  __jit_debug_descriptor.first_entry_ = entry;
-  __jit_debug_descriptor.relevant_entry_ = entry;
-  __jit_debug_descriptor.action_flag_ = JIT_REGISTER_FN;
-  __jit_debug_descriptor_timestamp++;
-  (*__jit_debug_register_code_ptr)();
+  descriptor.first_entry_ = entry;
+  descriptor.relevant_entry_ = entry;
+  descriptor.action_flag_ = JIT_REGISTER_FN;
+  descriptor.action_timestamp_ = entry->register_timestamp_;
+  UnlockActionCounter(descriptor, old_action_counter);
+
+  (*register_code_ptr)();
   return entry;
 }
 
-void DeleteJITCodeEntry(JITCodeEntry* entry) {
+static void DeleteJITCodeEntryInternal(
+    JITDescriptor& descriptor,
+    void (*register_code_ptr)(),
+    JITCodeEntry* entry)
+    REQUIRES(Locks::native_debug_interface_lock_) {
+  CHECK(entry != nullptr);
+  int32_t old_action_counter = LockActionCounter(descriptor);
+
   if (entry->prev_ != nullptr) {
     entry->prev_->next_ = entry->next_;
   } else {
-    __jit_debug_descriptor.first_entry_ = entry->next_;
+    descriptor.first_entry_ = entry->next_;
   }
-
   if (entry->next_ != nullptr) {
     entry->next_->prev_ = entry->prev_;
   }
 
-  g_jit_debug_mem_usage -= sizeof(JITCodeEntry) + entry->symfile_size_;
-  __jit_debug_descriptor.relevant_entry_ = entry;
-  __jit_debug_descriptor.action_flag_ = JIT_UNREGISTER_FN;
-  __jit_debug_descriptor_timestamp++;
-  (*__jit_debug_register_code_ptr)();
-  delete[] entry->symfile_addr_;
+  descriptor.relevant_entry_ = entry;
+  descriptor.action_flag_ = JIT_UNREGISTER_FN;
+  descriptor.action_timestamp_ = NanoTime();
+  UnlockActionCounter(descriptor, old_action_counter);
+
+  (*register_code_ptr)();
   delete entry;
 }
 
-// Mapping from code address to entry. Used to manage life-time of the entries.
-static std::unordered_map<uintptr_t, JITCodeEntry*> g_jit_code_entries
+static std::unordered_map<const void*, JITCodeEntry*> __dex_debug_entries
     GUARDED_BY(Locks::native_debug_interface_lock_);
 
-void IncrementJITCodeEntryRefcount(JITCodeEntry* entry, uintptr_t code_address) {
-  DCHECK(entry != nullptr);
-  DCHECK_EQ(g_jit_code_entries.count(code_address), 0u);
-  entry->ref_count++;
-  g_jit_code_entries.emplace(code_address, entry);
-}
-
-void DecrementJITCodeEntryRefcount(JITCodeEntry* entry, uintptr_t code_address) {
-  DCHECK(entry != nullptr);
-  DCHECK(g_jit_code_entries[code_address] == entry);
-  if (--entry->ref_count == 0) {
-    DeleteJITCodeEntry(entry);
+void AddNativeDebugInfoForDex(Thread* current_thread, ArrayRef<const uint8_t> dexfile) {
+  MutexLock mu(current_thread, *Locks::native_debug_interface_lock_);
+  DCHECK(dexfile.data() != nullptr);
+  // This is just defensive check. The class linker should not register the dex file twice.
+  if (__dex_debug_entries.count(dexfile.data()) == 0) {
+    JITCodeEntry* entry = CreateJITCodeEntryInternal(__dex_debug_descriptor,
+                                                     __dex_debug_register_code_ptr,
+                                                     dexfile);
+    __dex_debug_entries.emplace(dexfile.data(), entry);
   }
-  g_jit_code_entries.erase(code_address);
 }
 
-JITCodeEntry* GetJITCodeEntry(uintptr_t code_address) {
-  auto it = g_jit_code_entries.find(code_address);
-  return it == g_jit_code_entries.end() ? nullptr : it->second;
+void RemoveNativeDebugInfoForDex(Thread* current_thread, ArrayRef<const uint8_t> dexfile) {
+  MutexLock mu(current_thread, *Locks::native_debug_interface_lock_);
+  auto it = __dex_debug_entries.find(dexfile.data());
+  // We register dex files in the class linker and free them in DexFile_closeDexFile, but
+  // there might be cases where we load the dex file without using it in the class linker.
+  if (it != __dex_debug_entries.end()) {
+    DeleteJITCodeEntryInternal(__dex_debug_descriptor,
+                               __dex_debug_register_code_ptr,
+                               it->second);
+    __dex_debug_entries.erase(it);
+  }
 }
 
-size_t GetJITCodeEntryMemUsage() {
-  return g_jit_debug_mem_usage + g_jit_code_entries.size() * 2 * sizeof(void*);
+static size_t __jit_debug_mem_usage
+    GUARDED_BY(Locks::native_debug_interface_lock_) = 0;
+
+// Mapping from handle to entry. Used to manage life-time of the entries.
+static std::unordered_map<const void*, JITCodeEntry*> __jit_debug_entries
+    GUARDED_BY(Locks::native_debug_interface_lock_);
+
+void AddNativeDebugInfoForJit(const void* handle, const std::vector<uint8_t>& symfile) {
+  DCHECK_NE(symfile.size(), 0u);
+
+  // Make a copy of the buffer to shrink it and to pass ownership to JITCodeEntry.
+  uint8_t* copy = new uint8_t[symfile.size()];
+  CHECK(copy != nullptr);
+  memcpy(copy, symfile.data(), symfile.size());
+
+  JITCodeEntry* entry = CreateJITCodeEntryInternal(
+      __jit_debug_descriptor,
+      __jit_debug_register_code_ptr,
+      ArrayRef<const uint8_t>(copy, symfile.size()));
+  __jit_debug_mem_usage += sizeof(JITCodeEntry) + entry->symfile_size_;
+
+  // We don't provide handle for type debug info, which means we cannot free it later.
+  // (this only happens when --generate-debug-info flag is enabled for the purpose
+  // of being debugged with gdb; it does not happen for debuggable apps by default).
+  bool ok = handle == nullptr || __jit_debug_entries.emplace(handle, entry).second;
+  DCHECK(ok) << "Native debug entry already exists for " << std::hex << handle;
+}
+
+void RemoveNativeDebugInfoForJit(const void* handle) {
+  auto it = __jit_debug_entries.find(handle);
+  // We generate JIT native debug info only if the right runtime flags are enabled,
+  // but we try to remove it unconditionally whenever code is freed from JIT cache.
+  if (it != __jit_debug_entries.end()) {
+    JITCodeEntry* entry = it->second;
+    const uint8_t* symfile_addr = entry->symfile_addr_;
+    uint64_t symfile_size = entry->symfile_size_;
+    DeleteJITCodeEntryInternal(__jit_debug_descriptor,
+                               __jit_debug_register_code_ptr,
+                               entry);
+    __jit_debug_entries.erase(it);
+    __jit_debug_mem_usage -= sizeof(JITCodeEntry) + symfile_size;
+    delete[] symfile_addr;
+  }
+}
+
+size_t GetJitNativeDebugInfoMemUsage() {
+  return __jit_debug_mem_usage + __jit_debug_entries.size() * 2 * sizeof(void*);
 }
 
 }  // namespace art