Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 46756821 authored by Christopher Ferris's avatar Christopher Ferris
Browse files

Rewrite libbacktrace to be all C++.

This includes removing the map_info.c source and replacing it with the
BacktraceMap class to handle all map related code.

Change all callers of libbacktrace map functionality.

Also modify the corkscrew thread code so that it doesn't need to build
the map twice (once in the corkscrew format and once in the libbacktrace
format).

Change-Id: I32865a39f83a3dd6f958fc03c2759ba47d12382e
parent e847f429
Loading
Loading
Loading
Loading
+40 −38
Original line number Diff line number Diff line
@@ -33,6 +33,7 @@
#include <cutils/properties.h>

#include <backtrace/Backtrace.h>
#include <backtrace/BacktraceMap.h>

#include <sys/socket.h>
#include <linux/un.h>
@@ -230,9 +231,12 @@ static void dump_stack_segment(
      break;
    }

    const char* map_name = backtrace->GetMapName(stack_content, NULL);
    if (!map_name) {
    const backtrace_map_t* map = backtrace->FindMap(stack_content);
    const char* map_name;
    if (!map) {
      map_name = "";
    } else {
      map_name = map->name.c_str();
    }
    uintptr_t offset = 0;
    std::string func_name(backtrace->GetFunctionName(stack_content, &offset));
@@ -328,17 +332,17 @@ static void dump_backtrace_and_stack(Backtrace* backtrace, log_t* log, int scope
  }
}

static void dump_map(log_t* log, const backtrace_map_info_t* m, const char* what, int scope_flags) {
  if (m != NULL) {
    _LOG(log, scope_flags, "    %08x-%08x %c%c%c %s\n", m->start, m->end,
         m->is_readable ? 'r' : '-', m->is_writable ? 'w' : '-',
         m->is_executable ? 'x' : '-', m->name);
static void dump_map(log_t* log, const backtrace_map_t* map, const char* what, int scope_flags) {
  if (map != NULL) {
    _LOG(log, scope_flags, "    %08x-%08x %c%c%c %s\n", map->start, map->end,
         (map->flags & PROT_READ) ? 'r' : '-', (map->flags & PROT_WRITE) ? 'w' : '-',
         (map->flags & PROT_EXEC) ? 'x' : '-', map->name.c_str());
  } else {
    _LOG(log, scope_flags, "    (no %s)\n", what);
  }
}

static void dump_nearby_maps(const backtrace_map_info_t* map_info_list, log_t* log, pid_t tid, int scope_flags) {
static void dump_nearby_maps(BacktraceMap* map, log_t* log, pid_t tid, int scope_flags) {
  scope_flags |= SCOPE_SENSITIVE;
  siginfo_t si;
  memset(&si, 0, sizeof(si));
@@ -350,7 +354,7 @@ static void dump_nearby_maps(const backtrace_map_info_t* map_info_list, log_t* l
    return;
  }

  uintptr_t addr = (uintptr_t) si.si_addr;
  uintptr_t addr = reinterpret_cast<uintptr_t>(si.si_addr);
  addr &= ~0xfff;     // round to 4K page boundary
  if (addr == 0) {    // null-pointer deref
    return;
@@ -361,29 +365,26 @@ static void dump_nearby_maps(const backtrace_map_info_t* map_info_list, log_t* l

  // Search for a match, or for a hole where the match would be.  The list
  // is backward from the file content, so it starts at high addresses.
  const backtrace_map_info_t* map = map_info_list;
  const backtrace_map_info_t* next = NULL;
  const backtrace_map_info_t* prev = NULL;
  while (map != NULL) {
    if (addr >= map->start && addr < map->end) {
      next = map->next;
      break;
    } else if (addr >= map->end) {
      // map would be between "prev" and this entry
      next = map;
      map = NULL;
  const backtrace_map_t* cur_map = NULL;
  const backtrace_map_t* next_map = NULL;
  const backtrace_map_t* prev_map = NULL;
  for (BacktraceMap::const_iterator it = map->begin(); it != map->end(); ++it) {
    if (addr >= it->start && addr < it->end) {
      cur_map = &*it;
      if (it != map->begin()) {
        prev_map = &*(it-1);
      }
      if (++it != map->end()) {
        next_map = &*it;
      }
      break;
    }

    prev = map;
    map = map->next;
  }

  // Show "next" then "match" then "prev" so that the addresses appear in
  // ascending order (like /proc/pid/maps).
  dump_map(log, next, "map below", scope_flags);
  dump_map(log, map, "map for address", scope_flags);
  dump_map(log, prev, "map above", scope_flags);
  // Show the map address in ascending order (like /proc/pid/maps).
  dump_map(log, prev_map, "map below", scope_flags);
  dump_map(log, cur_map, "map for address", scope_flags);
  dump_map(log, next_map, "map above", scope_flags);
}

static void dump_thread(
@@ -394,13 +395,13 @@ static void dump_thread(
  dump_backtrace_and_stack(backtrace, log, scope_flags);
  if (IS_AT_FAULT(scope_flags)) {
    dump_memory_and_code(log, backtrace->Tid(), scope_flags);
    dump_nearby_maps(backtrace->GetMapList(), log, backtrace->Tid(), scope_flags);
    dump_nearby_maps(backtrace->GetMap(), log, backtrace->Tid(), scope_flags);
  }
}

// Return true if some thread is not detached cleanly
static bool dump_sibling_thread_report(
    log_t* log, pid_t pid, pid_t tid, int* total_sleep_time_usec, backtrace_map_info_t* map_info) {
    log_t* log, pid_t pid, pid_t tid, int* total_sleep_time_usec, BacktraceMap* map) {
  char task_path[64];
  snprintf(task_path, sizeof(task_path), "/proc/%d/task", pid);

@@ -434,7 +435,7 @@ static bool dump_sibling_thread_report(
    _LOG(log, 0, "--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---\n");
    dump_thread_info(log, pid, new_tid, 0);

    UniquePtr<Backtrace> backtrace(Backtrace::Create(pid, new_tid, map_info));
    UniquePtr<Backtrace> backtrace(Backtrace::Create(pid, new_tid, map));
    if (backtrace->Unwind(0)) {
      dump_thread(backtrace.get(), log, 0, total_sleep_time_usec);
    }
@@ -637,11 +638,12 @@ static bool dump_crash(log_t* log, pid_t pid, pid_t tid, int signal, uintptr_t a
    dump_fault_addr(log, tid, signal);
  }

  // Gather the map info once for all this process' threads.
  backtrace_map_info_t* map_info = backtrace_create_map_info_list(pid);

  UniquePtr<Backtrace> backtrace(Backtrace::Create(pid, tid, map_info));
  BacktraceMap* map = NULL;
  UniquePtr<Backtrace> backtrace(Backtrace::Create(pid, tid));
  if (backtrace->Unwind(0)) {
    // Grab the map that was created and share it with the siblings.
    map = backtrace->TakeMapOwnership();

    dump_abort_message(backtrace.get(), log, abort_msg_address);
    dump_thread(backtrace.get(), log, SCOPE_AT_FAULT, total_sleep_time_usec);
  }
@@ -652,11 +654,11 @@ static bool dump_crash(log_t* log, pid_t pid, pid_t tid, int signal, uintptr_t a

  bool detach_failed = false;
  if (dump_sibling_threads) {
    detach_failed = dump_sibling_thread_report(log, pid, tid, total_sleep_time_usec, map_info);
    detach_failed = dump_sibling_thread_report(log, pid, tid, total_sleep_time_usec, map);
  }

  // Destroy the previously created map info.
  backtrace_destroy_map_info_list(map_info);
  // Destroy the BacktraceMap object.
  delete map;

  if (want_logs) {
    dump_logs(log, pid, false);
+47 −25
Original line number Diff line number Diff line
@@ -17,10 +17,25 @@
#ifndef _BACKTRACE_BACKTRACE_H
#define _BACKTRACE_BACKTRACE_H

#include <backtrace/backtrace.h>
#include <stdint.h>

#include <string>
#include <vector>

#include <backtrace/backtrace_constants.h>
#include <backtrace/BacktraceMap.h>

struct backtrace_frame_data_t {
  size_t num;             // The current fame number.
  uintptr_t pc;           // The absolute pc.
  uintptr_t sp;           // The top of the stack.
  size_t stack_size;      // The size of the stack, zero indicate an unknown stack size.
  const backtrace_map_t* map;   // The map associated with the given pc.
  std::string func_name;  // The function name associated with this pc, NULL if not found.
  uintptr_t func_offset;  // pc relative to the start of the function, only valid if func_name is not NULL.
};

// Forward declarations.
class BacktraceImpl;

class Backtrace {
@@ -33,9 +48,9 @@ public:
  // If pid >= 0 and tid < 0, then the Backtrace object corresponds to a
  // different process.
  // Tracing a thread in a different process is not supported.
  // If map_info is NULL, then create the map and manage it internally.
  // If map_info is not NULL, the map is still owned by the caller.
  static Backtrace* Create(pid_t pid, pid_t tid, backtrace_map_info_t* map_info = NULL);
  // If map is NULL, then create the map and manage it internally.
  // If map is not NULL, the map is still owned by the caller.
  static Backtrace* Create(pid_t pid, pid_t tid, BacktraceMap* map = NULL);

  virtual ~Backtrace();

@@ -46,13 +61,12 @@ public:
  // If the string is empty, then no valid function name was found.
  virtual std::string GetFunctionName(uintptr_t pc, uintptr_t* offset);

  // Get the name of the map associated with the given pc. If NULL is returned,
  // then map_start is not set. Otherwise, map_start is the beginning of this
  // map.
  virtual const char* GetMapName(uintptr_t pc, uintptr_t* map_start);
  // Find the map associated with the given pc.
  virtual const backtrace_map_t* FindMap(uintptr_t pc);

  // Finds the memory map associated with the given ptr.
  virtual const backtrace_map_info_t* FindMapInfo(uintptr_t ptr);
  // Take ownership of the BacktraceMap object associated with the backtrace.
  // If this is called, the caller must handle deleting the object themselves.
  virtual BacktraceMap* TakeMapOwnership();

  // Read the data at a specific address.
  virtual bool ReadWord(uintptr_t ptr, uint32_t* out_value) = 0;
@@ -62,35 +76,43 @@ public:
  virtual std::string FormatFrameData(size_t frame_num);
  virtual std::string FormatFrameData(const backtrace_frame_data_t* frame);

  pid_t Pid() { return backtrace_.pid; }
  pid_t Tid() { return backtrace_.tid; }
  size_t NumFrames() { return backtrace_.num_frames; }

  const backtrace_t* GetBacktrace() { return &backtrace_; }
  pid_t Pid() { return pid_; }
  pid_t Tid() { return tid_; }
  size_t NumFrames() { return frames_.size(); }

  const backtrace_frame_data_t* GetFrame(size_t frame_num) {
    if (frame_num > NumFrames()) {
    if (frame_num >= frames_.size()) {
      return NULL;
    }
    return &backtrace_.frames[frame_num];
    return &frames_[frame_num];
  }

  const backtrace_map_info_t* GetMapList() {
    return map_info_;
  }
  typedef std::vector<backtrace_frame_data_t>::iterator iterator;
  iterator begin() { return frames_.begin(); }
  iterator end() { return frames_.end(); }

  typedef std::vector<backtrace_frame_data_t>::const_iterator const_iterator;
  const_iterator begin() const { return frames_.begin(); }
  const_iterator end() const { return frames_.end(); }

  BacktraceMap* GetMap() { return map_; }

protected:
  Backtrace(BacktraceImpl* impl, pid_t pid, backtrace_map_info_t* map_info);
  Backtrace(BacktraceImpl* impl, pid_t pid, BacktraceMap* map);

  virtual bool VerifyReadWordArgs(uintptr_t ptr, uint32_t* out_value);

  BacktraceImpl* impl_;
  bool BuildMap();

  backtrace_map_info_t* map_info_;
  pid_t pid_;
  pid_t tid_;

  bool map_info_requires_delete_;
  BacktraceMap* map_;
  bool map_shared_;

  backtrace_t backtrace_;
  std::vector<backtrace_frame_data_t> frames_;

  BacktraceImpl* impl_;

  friend class BacktraceImpl;
};
+72 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2014 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 _BACKTRACE_BACKTRACE_MAP_H
#define _BACKTRACE_BACKTRACE_MAP_H

#include <stdint.h>
#include <sys/mman.h>

#include <string>
#include <vector>

struct backtrace_map_t {
  uintptr_t start;
  uintptr_t end;
  int flags;
  std::string name;
};

class BacktraceMap {
public:
  BacktraceMap(pid_t pid);
  virtual ~BacktraceMap();

  // Get the map data structure for the given address.
  const backtrace_map_t* Find(uintptr_t addr);

  // The flags returned are the same flags as used by the mmap call.
  // The values are PROT_*.
  int GetFlags(uintptr_t pc) {
    const backtrace_map_t* map = Find(pc);
    if (map) {
      return map->flags;
    }
    return PROT_NONE;
  }

  bool IsReadable(uintptr_t pc) { return GetFlags(pc) & PROT_READ; }
  bool IsWritable(uintptr_t pc) { return GetFlags(pc) & PROT_WRITE; }
  bool IsExecutable(uintptr_t pc) { return GetFlags(pc) & PROT_EXEC; }

  typedef std::vector<backtrace_map_t>::iterator iterator;
  iterator begin() { return maps_.begin(); }
  iterator end() { return maps_.end(); }

  typedef std::vector<backtrace_map_t>::const_iterator const_iterator;
  const_iterator begin() const { return maps_.begin(); }
  const_iterator end() const { return maps_.end(); }

  virtual bool Build();

protected:
  virtual bool ParseLine(const char* line, backtrace_map_t* map);

  std::vector<backtrace_map_t> maps_;
  pid_t pid_;
};

#endif // _BACKTRACE_BACKTRACE_MAP_H

include/backtrace/backtrace.h

deleted100644 → 0
+0 −131
Original line number Diff line number Diff line
/*
 * Copyright (C) 2013 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 _BACKTRACE_H
#define _BACKTRACE_H

#include <stdint.h>
#include <stdbool.h>
#include <sys/types.h>

__BEGIN_DECLS

// When the pid to be traced is set to this value, then trace the current
// process. If the tid value is not BACKTRACE_NO_TID, then the specified
// thread from the current process will be traced.
#define BACKTRACE_CURRENT_PROCESS -1
// When the tid to be traced is set to this value, then trace the specified
// current thread of the specified pid.
#define BACKTRACE_CURRENT_THREAD -1

#define MAX_BACKTRACE_FRAMES 64

typedef struct backtrace_map_info {
  struct backtrace_map_info* next;
  uintptr_t start;
  uintptr_t end;
  bool is_readable;
  bool is_writable;
  bool is_executable;
  char name[];
} backtrace_map_info_t;

typedef struct {
  size_t num;             /* The current fame number. */
  uintptr_t pc;           /* The absolute pc. */
  uintptr_t sp;           /* The top of the stack. */
  size_t stack_size;      /* The size of the stack, zero indicate an unknown stack size. */
  const char* map_name;   /* The name of the map to which this pc belongs, NULL indicates the pc doesn't belong to a known map. */
  uintptr_t map_offset;   /* pc relative to the start of the map, only valid if map_name is not NULL. */
  char* func_name;        /* The function name associated with this pc, NULL if not found. */
  uintptr_t func_offset;  /* pc relative to the start of the function, only valid if func_name is not NULL. */
} backtrace_frame_data_t;

typedef struct {
  backtrace_frame_data_t frames[MAX_BACKTRACE_FRAMES];
  size_t num_frames;

  pid_t pid;
  pid_t tid;
  backtrace_map_info_t* map_info_list;
} backtrace_t;

typedef struct {
  void* data;
  const backtrace_t* backtrace;
} backtrace_context_t;

/* Create a context for the backtrace data and gather the backtrace.
 * If pid < 0, then gather the backtrace for the current process.
 */
bool backtrace_create_context(
    backtrace_context_t* context, pid_t pid, pid_t tid, size_t num_ignore_frames);

/* The same as backtrace_create_context, except that it is assumed that
 * the pid map has already been acquired and the caller will handle freeing
 * the map data.
 */
bool backtrace_create_context_with_map(
    backtrace_context_t* context, pid_t pid, pid_t tid, size_t num_ignore_frames,
    backtrace_map_info_t* map_info);

/* Gather the backtrace data for a pthread instead of a process. */
bool backtrace_create_thread_context(
    backtrace_context_t* context, pid_t tid, size_t num_ignore_frames);

/* Free any memory allocated during the context create. */
void backtrace_destroy_context(backtrace_context_t* context);

/* Read data at a specific address for a process. */
bool backtrace_read_word(
    const backtrace_context_t* context, uintptr_t ptr, uint32_t* value);

/* Get information about the map name associated with a pc. If NULL is
 * returned, then map_start is not set.
 */
const char* backtrace_get_map_name(
    const backtrace_context_t* context, uintptr_t pc, uintptr_t* map_start);

/* Get the function name and offset given the pc. If NULL is returned,
 * then func_offset is not set. The returned string is allocated using
 * malloc and must be freed by the caller.
 */
char* backtrace_get_func_name(
    const backtrace_context_t* context, uintptr_t pc, uintptr_t* func_offset);

/* Loads memory map from /proc/<pid>/maps. If pid < 0, then load the memory
 * map for the current process.
 */
backtrace_map_info_t* backtrace_create_map_info_list(pid_t pid);

/* Frees memory associated with the map list. */
void backtrace_destroy_map_info_list(backtrace_map_info_t* map_info_list);

/* Finds the memory map that contains the specified pc. */
const backtrace_map_info_t* backtrace_find_map_info(
    const backtrace_map_info_t* map_info_list, uintptr_t pc);

/* Create a formatted line of backtrace information for a single frame. */
void backtrace_format_frame_data(
    const backtrace_context_t* context, size_t frame_num, char* buf,
    size_t buf_size);

/* Get the backtrace data structure associated with the context. */
const backtrace_t* backtrace_get_data(backtrace_context_t* context);

__END_DECLS

#endif /* _BACKTRACE_H */
+30 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2014 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 _BACKTRACE_BACKTRACE_CONSTANTS_H
#define _BACKTRACE_BACKTRACE_CONSTANTS_H

// When the pid to be traced is set to this value, then trace the current
// process. If the tid value is not BACKTRACE_NO_TID, then the specified
// thread from the current process will be traced.
#define BACKTRACE_CURRENT_PROCESS -1
// When the tid to be traced is set to this value, then trace the specified
// current thread of the specified pid.
#define BACKTRACE_CURRENT_THREAD -1

#define MAX_BACKTRACE_FRAMES 64

#endif // _BACKTRACE_BACKTRACE_CONSTANTS_H
Loading