#include "bugsnag_register_reader.h"

#include <string.h>
#include <iomanip>
#include <map>
#include <sstream>
#include <string>
#include <vector>

#include "google_breakpad/processor/stack_frame_cpu.h"

using google_breakpad::StackFrame;

namespace bugsnag_breakpad {

const bool PAD_REGISTER_ADDRESSES = false;

static std::string FormatRegisterValue(uint32_t value) {
  char buffer[64];
  if (PAD_REGISTER_ADDRESSES) {
    snprintf(buffer, sizeof(buffer), "0x%08x", value);
  } else {
    snprintf(buffer, sizeof(buffer), "0x%x", value);
  }

  std::stringstream ss;
  ss << buffer;
  return ss.str();
}

static std::string FormatRegister64Value(uint64_t value) {
  char buffer[64];
  if (PAD_REGISTER_ADDRESSES) {
    snprintf(buffer, sizeof(buffer), "0x%016" PRIx64 , value);
  } else {
    snprintf(buffer, sizeof(buffer), "0x%" PRIx64, value);
  }

  std::stringstream ss;
  ss << buffer;
  return ss.str();
}

static void AddToRegisterMap(std::map<std::string, std::string>& registerMap,
                             const char* name,
                             const std::string value) {
  registerMap.insert(std::make_pair(name, value));
}

std::map<std::string, std::string> getRegisterValues(const StackFrame* frame,
                                                     const std::string& cpu) {
  std::map<std::string, std::string> registerMap;
  if (cpu == "x86") {
    using google_breakpad::StackFrameX86;
    const StackFrameX86* frame_x86 =
        reinterpret_cast<const StackFrameX86*>(frame);

    if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_EIP) {
      AddToRegisterMap(registerMap, "eip",
                       FormatRegisterValue(frame_x86->context.eip));
    }
    if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_ESP) {
      AddToRegisterMap(registerMap, "esp",
                       FormatRegisterValue(frame_x86->context.esp));
    }
    if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_EBP) {
      AddToRegisterMap(registerMap, "ebp",
                       FormatRegisterValue(frame_x86->context.ebp));
    }
    if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_EBX) {
      AddToRegisterMap(registerMap, "ebx",
                       FormatRegisterValue(frame_x86->context.ebx));
    }
    if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_ESI) {
      AddToRegisterMap(registerMap, "esi",
                       FormatRegisterValue(frame_x86->context.esi));
    }
    if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_EDI) {
      AddToRegisterMap(registerMap, "edi",
                       FormatRegisterValue(frame_x86->context.edi));
    }
    if (frame_x86->context_validity == StackFrameX86::CONTEXT_VALID_ALL) {
      AddToRegisterMap(registerMap, "eax",
                       FormatRegisterValue(frame_x86->context.eax));
      AddToRegisterMap(registerMap, "ecx",
                       FormatRegisterValue(frame_x86->context.ecx));
      AddToRegisterMap(registerMap, "edx",
                       FormatRegisterValue(frame_x86->context.edx));
      AddToRegisterMap(registerMap, "efl",
                       FormatRegisterValue(frame_x86->context.eflags));
    }
  } else if (cpu == "ppc") {
    using google_breakpad::StackFramePPC;
    const StackFramePPC* frame_ppc =
        reinterpret_cast<const StackFramePPC*>(frame);

    if (frame_ppc->context_validity & StackFramePPC::CONTEXT_VALID_SRR0) {
      AddToRegisterMap(registerMap, "srr0",
                       FormatRegisterValue(frame_ppc->context.srr0));
    }
    if (frame_ppc->context_validity & StackFramePPC::CONTEXT_VALID_GPR1) {
      AddToRegisterMap(registerMap, "r1",
                       FormatRegisterValue(frame_ppc->context.gpr[1]));
    }
  } else if (cpu == "amd64") {
    using google_breakpad::StackFrameAMD64;
    const StackFrameAMD64* frame_amd64 =
        reinterpret_cast<const StackFrameAMD64*>(frame);

    if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RAX) {
      AddToRegisterMap(registerMap, "rax",
                       FormatRegister64Value(frame_amd64->context.rax));
    }
    if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RDX) {
      AddToRegisterMap(registerMap, "rdx",
                       FormatRegister64Value(frame_amd64->context.rdx));
    }
    if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RCX) {
      AddToRegisterMap(registerMap, "rcx",
                       FormatRegister64Value(frame_amd64->context.rcx));
    }
    if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RBX) {
      AddToRegisterMap(registerMap, "rbx",
                       FormatRegister64Value(frame_amd64->context.rbx));
    }
    if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RSI) {
      AddToRegisterMap(registerMap, "rsi",
                       FormatRegister64Value(frame_amd64->context.rsi));
    }
    if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RDI) {
      AddToRegisterMap(registerMap, "rdi",
                       FormatRegister64Value(frame_amd64->context.rdi));
    }
    if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RBP) {
      AddToRegisterMap(registerMap, "rbp",
                       FormatRegister64Value(frame_amd64->context.rbp));
    }
    if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RSP) {
      AddToRegisterMap(registerMap, "rsp",
                       FormatRegister64Value(frame_amd64->context.rsp));
    }
    if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R8) {
      AddToRegisterMap(registerMap, "r8",
                       FormatRegister64Value(frame_amd64->context.r8));
    }
    if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R9) {
      AddToRegisterMap(registerMap, "r9",
                       FormatRegister64Value(frame_amd64->context.r9));
    }
    if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R10) {
      AddToRegisterMap(registerMap, "r10",
                       FormatRegister64Value(frame_amd64->context.r10));
    }
    if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R11) {
      AddToRegisterMap(registerMap, "r11",
                       FormatRegister64Value(frame_amd64->context.r11));
    }
    if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R12) {
      AddToRegisterMap(registerMap, "r12",
                       FormatRegister64Value(frame_amd64->context.r12));
    }
    if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R13) {
      AddToRegisterMap(registerMap, "r13",
                       FormatRegister64Value(frame_amd64->context.r13));
    }
    if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R14) {
      AddToRegisterMap(registerMap, "r14",
                       FormatRegister64Value(frame_amd64->context.r14));
    }
    if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R15) {
      AddToRegisterMap(registerMap, "r15",
                       FormatRegister64Value(frame_amd64->context.r15));
    }
    if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RIP) {
      AddToRegisterMap(registerMap, "rip",
                       FormatRegister64Value(frame_amd64->context.rip));
    }
  } else if (cpu == "sparc") {
    using google_breakpad::StackFrameSPARC;
    const StackFrameSPARC* frame_sparc =
        reinterpret_cast<const StackFrameSPARC*>(frame);

    if (frame_sparc->context_validity & StackFrameSPARC::CONTEXT_VALID_SP) {
      AddToRegisterMap(registerMap, "sp",
                       FormatRegisterValue(frame_sparc->context.g_r[14]));
    }
    if (frame_sparc->context_validity & StackFrameSPARC::CONTEXT_VALID_FP) {
      AddToRegisterMap(registerMap, "fp",
                       FormatRegisterValue(frame_sparc->context.g_r[30]));
    }
    if (frame_sparc->context_validity & StackFrameSPARC::CONTEXT_VALID_PC) {
      AddToRegisterMap(registerMap, "pc",
                       FormatRegisterValue(frame_sparc->context.pc));
    }
  } else if (cpu == "arm") {
    using google_breakpad::StackFrameARM;
    const StackFrameARM* frame_arm =
        reinterpret_cast<const StackFrameARM*>(frame);

    if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R0) {
      AddToRegisterMap(registerMap, "r0",
                       FormatRegisterValue(frame_arm->context.iregs[0]));
    }
    if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R1) {
      AddToRegisterMap(registerMap, "r1",
                       FormatRegisterValue(frame_arm->context.iregs[1]));
    }
    if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R2) {
      AddToRegisterMap(registerMap, "r2",
                       FormatRegisterValue(frame_arm->context.iregs[2]));
    }
    if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R3) {
      AddToRegisterMap(registerMap, "r3",
                       FormatRegisterValue(frame_arm->context.iregs[3]));
    }

    if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R4) {
      AddToRegisterMap(registerMap, "r4",
                       FormatRegisterValue(frame_arm->context.iregs[4]));
    }
    if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R5) {
      AddToRegisterMap(registerMap, "r5",
                       FormatRegisterValue(frame_arm->context.iregs[5]));
    }
    if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R6) {
      AddToRegisterMap(registerMap, "r6",
                       FormatRegisterValue(frame_arm->context.iregs[6]));
    }
    if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R7) {
      AddToRegisterMap(registerMap, "r7",
                       FormatRegisterValue(frame_arm->context.iregs[7]));
    }
    if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R8) {
      AddToRegisterMap(registerMap, "r8",
                       FormatRegisterValue(frame_arm->context.iregs[8]));
    }
    if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R9) {
      AddToRegisterMap(registerMap, "r9",
                       FormatRegisterValue(frame_arm->context.iregs[9]));
    }
    if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R10) {
      AddToRegisterMap(registerMap, "r10",
                       FormatRegisterValue(frame_arm->context.iregs[10]));
    }
    if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R12) {
      AddToRegisterMap(registerMap, "r12",
                       FormatRegisterValue(frame_arm->context.iregs[12]));
    }

    if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_FP) {
      AddToRegisterMap(registerMap, "fp",
                       FormatRegisterValue(frame_arm->context.iregs[11]));
    }
    if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_SP) {
      AddToRegisterMap(registerMap, "sp",
                       FormatRegisterValue(frame_arm->context.iregs[13]));
    }
    if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_LR) {
      AddToRegisterMap(registerMap, "lr",
                       FormatRegisterValue(frame_arm->context.iregs[14]));
    }
    if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_PC) {
      AddToRegisterMap(registerMap, "pc",
                       FormatRegisterValue(frame_arm->context.iregs[15]));
    }
  } else if (cpu == "arm64") {
    using google_breakpad::StackFrameARM64;
    const StackFrameARM64* frame_arm64 =
        reinterpret_cast<const StackFrameARM64*>(frame);

    if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X0) {
      AddToRegisterMap(registerMap, "x0",
                       FormatRegister64Value(frame_arm64->context.iregs[0]));
    }
    if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X1) {
      AddToRegisterMap(registerMap, "x1",
                       FormatRegister64Value(frame_arm64->context.iregs[1]));
    }
    if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X2) {
      AddToRegisterMap(registerMap, "x2",
                       FormatRegister64Value(frame_arm64->context.iregs[2]));
    }
    if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X3) {
      AddToRegisterMap(registerMap, "x3",
                       FormatRegister64Value(frame_arm64->context.iregs[3]));
    }
    if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X4) {
      AddToRegisterMap(registerMap, "x4",
                       FormatRegister64Value(frame_arm64->context.iregs[4]));
    }
    if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X5) {
      AddToRegisterMap(registerMap, "x5",
                       FormatRegister64Value(frame_arm64->context.iregs[5]));
    }
    if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X6) {
      AddToRegisterMap(registerMap, "x6",
                       FormatRegister64Value(frame_arm64->context.iregs[6]));
    }
    if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X7) {
      AddToRegisterMap(registerMap, "x7",
                       FormatRegister64Value(frame_arm64->context.iregs[7]));
    }
    if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X8) {
      AddToRegisterMap(registerMap, "x8",
                       FormatRegister64Value(frame_arm64->context.iregs[8]));
    }
    if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X9) {
      AddToRegisterMap(registerMap, "x9",
                       FormatRegister64Value(frame_arm64->context.iregs[9]));
    }
    if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X10) {
      AddToRegisterMap(registerMap, "x10",
                       FormatRegister64Value(frame_arm64->context.iregs[10]));
    }
    if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X11) {
      AddToRegisterMap(registerMap, "x11",
                       FormatRegister64Value(frame_arm64->context.iregs[11]));
    }
    if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X12) {
      AddToRegisterMap(registerMap, "x12",
                       FormatRegister64Value(frame_arm64->context.iregs[12]));
    }
    if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X13) {
      AddToRegisterMap(registerMap, "x13",
                       FormatRegister64Value(frame_arm64->context.iregs[13]));
    }
    if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X14) {
      AddToRegisterMap(registerMap, "x14",
                       FormatRegister64Value(frame_arm64->context.iregs[14]));
    }
    if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X15) {
      AddToRegisterMap(registerMap, "x15",
                       FormatRegister64Value(frame_arm64->context.iregs[15]));
    }
    if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X16) {
      AddToRegisterMap(registerMap, "x16",
                       FormatRegister64Value(frame_arm64->context.iregs[16]));
    }
    if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X17) {
      AddToRegisterMap(registerMap, "x17",
                       FormatRegister64Value(frame_arm64->context.iregs[17]));
    }
    if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X18) {
      AddToRegisterMap(registerMap, "x18",
                       FormatRegister64Value(frame_arm64->context.iregs[18]));
    }
    if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X19) {
      AddToRegisterMap(registerMap, "x19",
                       FormatRegister64Value(frame_arm64->context.iregs[19]));
    }
    if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X20) {
      AddToRegisterMap(registerMap, "x20",
                       FormatRegister64Value(frame_arm64->context.iregs[20]));
    }
    if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X21) {
      AddToRegisterMap(registerMap, "x21",
                       FormatRegister64Value(frame_arm64->context.iregs[21]));
    }
    if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X22) {
      AddToRegisterMap(registerMap, "x22",
                       FormatRegister64Value(frame_arm64->context.iregs[22]));
    }
    if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X23) {
      AddToRegisterMap(registerMap, "x23",
                       FormatRegister64Value(frame_arm64->context.iregs[23]));
    }
    if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X24) {
      AddToRegisterMap(registerMap, "x24",
                       FormatRegister64Value(frame_arm64->context.iregs[24]));
    }
    if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X25) {
      AddToRegisterMap(registerMap, "x25",
                       FormatRegister64Value(frame_arm64->context.iregs[25]));
    }
    if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X26) {
      AddToRegisterMap(registerMap, "x26",
                       FormatRegister64Value(frame_arm64->context.iregs[26]));
    }
    if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X27) {
      AddToRegisterMap(registerMap, "x27",
                       FormatRegister64Value(frame_arm64->context.iregs[27]));
    }
    if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X28) {
      AddToRegisterMap(registerMap, "x28",
                       FormatRegister64Value(frame_arm64->context.iregs[28]));
    }

    if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_FP) {
      AddToRegisterMap(registerMap, "fp",
                       FormatRegister64Value(frame_arm64->context.iregs[29]));
    }
    if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_LR) {
      AddToRegisterMap(registerMap, "lr",
                       FormatRegister64Value(frame_arm64->context.iregs[30]));
    }
    if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_SP) {
      AddToRegisterMap(registerMap, "sp",
                       FormatRegister64Value(frame_arm64->context.iregs[31]));
    }
    if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_PC) {
      AddToRegisterMap(registerMap, "pc",
                       FormatRegister64Value(frame_arm64->context.iregs[32]));
    }
  } else if ((cpu == "mips") || (cpu == "mips64")) {
    using google_breakpad::StackFrameMIPS;
    const StackFrameMIPS* frame_mips =
        reinterpret_cast<const StackFrameMIPS*>(frame);

    if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_GP) {
      AddToRegisterMap(registerMap, "gp",
                       FormatRegister64Value(
                           frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_GP]));
    }
    if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_SP) {
      AddToRegisterMap(registerMap, "sp",
                       FormatRegister64Value(
                           frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_SP]));
    }
    if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_FP) {
      AddToRegisterMap(registerMap, "fp",
                       FormatRegister64Value(
                           frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_FP]));
    }
    if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_RA) {
      AddToRegisterMap(registerMap, "ra",
                       FormatRegister64Value(
                           frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_RA]));
    }
    if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_PC) {
      AddToRegisterMap(registerMap, "pc",
                       FormatRegister64Value(frame_mips->context.epc));
    }

    if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_S0) {
      AddToRegisterMap(registerMap, "s0",
                       FormatRegister64Value(
                           frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S0]));
    }
    if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_S1) {
      AddToRegisterMap(registerMap, "s1",
                       FormatRegister64Value(
                           frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S1]));
    }
    if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_S2) {
      AddToRegisterMap(registerMap, "s2",
                       FormatRegister64Value(
                           frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S2]));
    }
    if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_S3) {
      AddToRegisterMap(registerMap, "s3",
                       FormatRegister64Value(
                           frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S3]));
    }
    if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_S4) {
      AddToRegisterMap(registerMap, "s4",
                       FormatRegister64Value(
                           frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S4]));
    }
    if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_S5) {
      AddToRegisterMap(registerMap, "s5",
                       FormatRegister64Value(
                           frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S5]));
    }
    if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_S6) {
      AddToRegisterMap(registerMap, "s6",
                       FormatRegister64Value(
                           frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S6]));
    }
    if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_S7) {
      AddToRegisterMap(registerMap, "s7",
                       FormatRegister64Value(
                           frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S7]));
    }
  }
  return registerMap;
}

}
#include "bugsnag_register_reader.h"

#include <string.h>
#include <iomanip>
#include <map>
#include <sstream>
#include <string>
#include <vector>

#include "google_breakpad/processor/stack_frame_cpu.h"

using google_breakpad::StackFrame;

namespace bugsnag_breakpad {

// flag to indicate that register addresses should be padded with leading zeros
const bool PAD_REGISTER_ADDRESSES = false;

static std::string FormatRegisterValue(uint32_t value) {
  char buffer[64];
  if (PAD_REGISTER_ADDRESSES) {
    snprintf(buffer, sizeof(buffer), "0x%08x", value);
  } else {
    snprintf(buffer, sizeof(buffer), "0x%x", value);
  }

  std::stringstream ss;
  ss << buffer;
  return ss.str();
}

static std::string FormatRegister64Value(uint64_t value) {
  char buffer[64];
  if (PAD_REGISTER_ADDRESSES) {
    snprintf(buffer, sizeof(buffer), "0x%016" PRIx64 , value);
  } else {
    snprintf(buffer, sizeof(buffer), "0x%" PRIx64, value);
  }

  std::stringstream ss;
  ss << buffer;
  return ss.str();
}

static void AddToRegisterMap(std::map<std::string, std::string>& registerMap,
                             const char* name,
                             const std::string value) {
  registerMap.insert(std::make_pair(name, value));
}

/*
The logic to access register names and values has been derived from the function
PrintStack in stackwalk_common.cc
*/
std::map<std::string, std::string> getRegisterValues(const StackFrame* frame,
                                                     const std::string& cpu) {
  std::map<std::string, std::string> registerMap;
  if (cpu == "x86") {
    using google_breakpad::StackFrameX86;
    const StackFrameX86* frame_x86 =
        reinterpret_cast<const StackFrameX86*>(frame);

    if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_EIP) {
      AddToRegisterMap(registerMap, "eip",
                       FormatRegisterValue(frame_x86->context.eip));
    }
    if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_ESP) {
      AddToRegisterMap(registerMap, "esp",
                       FormatRegisterValue(frame_x86->context.esp));
    }
    if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_EBP) {
      AddToRegisterMap(registerMap, "ebp",
                       FormatRegisterValue(frame_x86->context.ebp));
    }
    if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_EBX) {
      AddToRegisterMap(registerMap, "ebx",
                       FormatRegisterValue(frame_x86->context.ebx));
    }
    if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_ESI) {
      AddToRegisterMap(registerMap, "esi",
                       FormatRegisterValue(frame_x86->context.esi));
    }
    if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_EDI) {
      AddToRegisterMap(registerMap, "edi",
                       FormatRegisterValue(frame_x86->context.edi));
    }
    if (frame_x86->context_validity == StackFrameX86::CONTEXT_VALID_ALL) {
      AddToRegisterMap(registerMap, "eax",
                       FormatRegisterValue(frame_x86->context.eax));
      AddToRegisterMap(registerMap, "ecx",
                       FormatRegisterValue(frame_x86->context.ecx));
      AddToRegisterMap(registerMap, "edx",
                       FormatRegisterValue(frame_x86->context.edx));
      AddToRegisterMap(registerMap, "efl",
                       FormatRegisterValue(frame_x86->context.eflags));
    }
  } else if (cpu == "ppc") {
    using google_breakpad::StackFramePPC;
    const StackFramePPC* frame_ppc =
        reinterpret_cast<const StackFramePPC*>(frame);

    if (frame_ppc->context_validity & StackFramePPC::CONTEXT_VALID_SRR0) {
      AddToRegisterMap(registerMap, "srr0",
                       FormatRegisterValue(frame_ppc->context.srr0));
    }
    if (frame_ppc->context_validity & StackFramePPC::CONTEXT_VALID_GPR1) {
      AddToRegisterMap(registerMap, "r1",
                       FormatRegisterValue(frame_ppc->context.gpr[1]));
    }
  } else if (cpu == "amd64") {
    using google_breakpad::StackFrameAMD64;
    const StackFrameAMD64* frame_amd64 =
        reinterpret_cast<const StackFrameAMD64*>(frame);

    if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RAX) {
      AddToRegisterMap(registerMap, "rax",
                       FormatRegister64Value(frame_amd64->context.rax));
    }
    if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RDX) {
      AddToRegisterMap(registerMap, "rdx",
                       FormatRegister64Value(frame_amd64->context.rdx));
    }
    if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RCX) {
      AddToRegisterMap(registerMap, "rcx",
                       FormatRegister64Value(frame_amd64->context.rcx));
    }
    if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RBX) {
      AddToRegisterMap(registerMap, "rbx",
                       FormatRegister64Value(frame_amd64->context.rbx));
    }
    if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RSI) {
      AddToRegisterMap(registerMap, "rsi",
                       FormatRegister64Value(frame_amd64->context.rsi));
    }
    if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RDI) {
      AddToRegisterMap(registerMap, "rdi",
                       FormatRegister64Value(frame_amd64->context.rdi));
    }
    if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RBP) {
      AddToRegisterMap(registerMap, "rbp",
                       FormatRegister64Value(frame_amd64->context.rbp));
    }
    if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RSP) {
      AddToRegisterMap(registerMap, "rsp",
                       FormatRegister64Value(frame_amd64->context.rsp));
    }
    if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R8) {
      AddToRegisterMap(registerMap, "r8",
                       FormatRegister64Value(frame_amd64->context.r8));
    }
    if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R9) {
      AddToRegisterMap(registerMap, "r9",
                       FormatRegister64Value(frame_amd64->context.r9));
    }
    if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R10) {
      AddToRegisterMap(registerMap, "r10",
                       FormatRegister64Value(frame_amd64->context.r10));
    }
    if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R11) {
      AddToRegisterMap(registerMap, "r11",
                       FormatRegister64Value(frame_amd64->context.r11));
    }
    if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R12) {
      AddToRegisterMap(registerMap, "r12",
                       FormatRegister64Value(frame_amd64->context.r12));
    }
    if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R13) {
      AddToRegisterMap(registerMap, "r13",
                       FormatRegister64Value(frame_amd64->context.r13));
    }
    if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R14) {
      AddToRegisterMap(registerMap, "r14",
                       FormatRegister64Value(frame_amd64->context.r14));
    }
    if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R15) {
      AddToRegisterMap(registerMap, "r15",
                       FormatRegister64Value(frame_amd64->context.r15));
    }
    if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RIP) {
      AddToRegisterMap(registerMap, "rip",
                       FormatRegister64Value(frame_amd64->context.rip));
    }
  } else if (cpu == "sparc") {
    using google_breakpad::StackFrameSPARC;
    const StackFrameSPARC* frame_sparc =
        reinterpret_cast<const StackFrameSPARC*>(frame);

    if (frame_sparc->context_validity & StackFrameSPARC::CONTEXT_VALID_SP) {
      AddToRegisterMap(registerMap, "sp",
                       FormatRegisterValue(frame_sparc->context.g_r[14]));
    }
    if (frame_sparc->context_validity & StackFrameSPARC::CONTEXT_VALID_FP) {
      AddToRegisterMap(registerMap, "fp",
                       FormatRegisterValue(frame_sparc->context.g_r[30]));
    }
    if (frame_sparc->context_validity & StackFrameSPARC::CONTEXT_VALID_PC) {
      AddToRegisterMap(registerMap, "pc",
                       FormatRegisterValue(frame_sparc->context.pc));
    }
  } else if (cpu == "arm") {
    using google_breakpad::StackFrameARM;
    const StackFrameARM* frame_arm =
        reinterpret_cast<const StackFrameARM*>(frame);

    // Argument registers (caller-saves), which will likely only be valid
    // for the youngest frame.
    if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R0) {
      AddToRegisterMap(registerMap, "r0",
                       FormatRegisterValue(frame_arm->context.iregs[0]));
    }
    if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R1) {
      AddToRegisterMap(registerMap, "r1",
                       FormatRegisterValue(frame_arm->context.iregs[1]));
    }
    if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R2) {
      AddToRegisterMap(registerMap, "r2",
                       FormatRegisterValue(frame_arm->context.iregs[2]));
    }
    if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R3) {
      AddToRegisterMap(registerMap, "r3",
                       FormatRegisterValue(frame_arm->context.iregs[3]));
    }

    // General-purpose callee-saves registers.
    if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R4) {
      AddToRegisterMap(registerMap, "r4",
                       FormatRegisterValue(frame_arm->context.iregs[4]));
    }
    if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R5) {
      AddToRegisterMap(registerMap, "r5",
                       FormatRegisterValue(frame_arm->context.iregs[5]));
    }
    if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R6) {
      AddToRegisterMap(registerMap, "r6",
                       FormatRegisterValue(frame_arm->context.iregs[6]));
    }
    if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R7) {
      AddToRegisterMap(registerMap, "r7",
                       FormatRegisterValue(frame_arm->context.iregs[7]));
    }
    if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R8) {
      AddToRegisterMap(registerMap, "r8",
                       FormatRegisterValue(frame_arm->context.iregs[8]));
    }
    if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R9) {
      AddToRegisterMap(registerMap, "r9",
                       FormatRegisterValue(frame_arm->context.iregs[9]));
    }
    if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R10) {
      AddToRegisterMap(registerMap, "r10",
                       FormatRegisterValue(frame_arm->context.iregs[10]));
    }
    if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R12) {
      AddToRegisterMap(registerMap, "r12",
                       FormatRegisterValue(frame_arm->context.iregs[12]));
    }

    // Registers with a dedicated or conventional purpose.
    if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_FP) {
      AddToRegisterMap(registerMap, "fp",
                       FormatRegisterValue(frame_arm->context.iregs[11]));
    }
    if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_SP) {
      AddToRegisterMap(registerMap, "sp",
                       FormatRegisterValue(frame_arm->context.iregs[13]));
    }
    if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_LR) {
      AddToRegisterMap(registerMap, "lr",
                       FormatRegisterValue(frame_arm->context.iregs[14]));
    }
    if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_PC) {
      AddToRegisterMap(registerMap, "pc",
                       FormatRegisterValue(frame_arm->context.iregs[15]));
    }
  } else if (cpu == "arm64") {
    using google_breakpad::StackFrameARM64;
    const StackFrameARM64* frame_arm64 =
        reinterpret_cast<const StackFrameARM64*>(frame);

    if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X0) {
      AddToRegisterMap(registerMap, "x0",
                       FormatRegister64Value(frame_arm64->context.iregs[0]));
    }
    if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X1) {
      AddToRegisterMap(registerMap, "x1",
                       FormatRegister64Value(frame_arm64->context.iregs[1]));
    }
    if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X2) {
      AddToRegisterMap(registerMap, "x2",
                       FormatRegister64Value(frame_arm64->context.iregs[2]));
    }
    if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X3) {
      AddToRegisterMap(registerMap, "x3",
                       FormatRegister64Value(frame_arm64->context.iregs[3]));
    }
    if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X4) {
      AddToRegisterMap(registerMap, "x4",
                       FormatRegister64Value(frame_arm64->context.iregs[4]));
    }
    if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X5) {
      AddToRegisterMap(registerMap, "x5",
                       FormatRegister64Value(frame_arm64->context.iregs[5]));
    }
    if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X6) {
      AddToRegisterMap(registerMap, "x6",
                       FormatRegister64Value(frame_arm64->context.iregs[6]));
    }
    if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X7) {
      AddToRegisterMap(registerMap, "x7",
                       FormatRegister64Value(frame_arm64->context.iregs[7]));
    }
    if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X8) {
      AddToRegisterMap(registerMap, "x8",
                       FormatRegister64Value(frame_arm64->context.iregs[8]));
    }
    if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X9) {
      AddToRegisterMap(registerMap, "x9",
                       FormatRegister64Value(frame_arm64->context.iregs[9]));
    }
    if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X10) {
      AddToRegisterMap(registerMap, "x10",
                       FormatRegister64Value(frame_arm64->context.iregs[10]));
    }
    if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X11) {
      AddToRegisterMap(registerMap, "x11",
                       FormatRegister64Value(frame_arm64->context.iregs[11]));
    }
    if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X12) {
      AddToRegisterMap(registerMap, "x12",
                       FormatRegister64Value(frame_arm64->context.iregs[12]));
    }
    if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X13) {
      AddToRegisterMap(registerMap, "x13",
                       FormatRegister64Value(frame_arm64->context.iregs[13]));
    }
    if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X14) {
      AddToRegisterMap(registerMap, "x14",
                       FormatRegister64Value(frame_arm64->context.iregs[14]));
    }
    if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X15) {
      AddToRegisterMap(registerMap, "x15",
                       FormatRegister64Value(frame_arm64->context.iregs[15]));
    }
    if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X16) {
      AddToRegisterMap(registerMap, "x16",
                       FormatRegister64Value(frame_arm64->context.iregs[16]));
    }
    if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X17) {
      AddToRegisterMap(registerMap, "x17",
                       FormatRegister64Value(frame_arm64->context.iregs[17]));
    }
    if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X18) {
      AddToRegisterMap(registerMap, "x18",
                       FormatRegister64Value(frame_arm64->context.iregs[18]));
    }
    if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X19) {
      AddToRegisterMap(registerMap, "x19",
                       FormatRegister64Value(frame_arm64->context.iregs[19]));
    }
    if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X20) {
      AddToRegisterMap(registerMap, "x20",
                       FormatRegister64Value(frame_arm64->context.iregs[20]));
    }
    if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X21) {
      AddToRegisterMap(registerMap, "x21",
                       FormatRegister64Value(frame_arm64->context.iregs[21]));
    }
    if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X22) {
      AddToRegisterMap(registerMap, "x22",
                       FormatRegister64Value(frame_arm64->context.iregs[22]));
    }
    if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X23) {
      AddToRegisterMap(registerMap, "x23",
                       FormatRegister64Value(frame_arm64->context.iregs[23]));
    }
    if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X24) {
      AddToRegisterMap(registerMap, "x24",
                       FormatRegister64Value(frame_arm64->context.iregs[24]));
    }
    if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X25) {
      AddToRegisterMap(registerMap, "x25",
                       FormatRegister64Value(frame_arm64->context.iregs[25]));
    }
    if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X26) {
      AddToRegisterMap(registerMap, "x26",
                       FormatRegister64Value(frame_arm64->context.iregs[26]));
    }
    if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X27) {
      AddToRegisterMap(registerMap, "x27",
                       FormatRegister64Value(frame_arm64->context.iregs[27]));
    }
    if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X28) {
      AddToRegisterMap(registerMap, "x28",
                       FormatRegister64Value(frame_arm64->context.iregs[28]));
    }

    // Registers with a dedicated or conventional purpose.
    if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_FP) {
      AddToRegisterMap(registerMap, "fp",
                       FormatRegister64Value(frame_arm64->context.iregs[29]));
    }
    if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_LR) {
      AddToRegisterMap(registerMap, "lr",
                       FormatRegister64Value(frame_arm64->context.iregs[30]));
    }
    if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_SP) {
      AddToRegisterMap(registerMap, "sp",
                       FormatRegister64Value(frame_arm64->context.iregs[31]));
    }
    if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_PC) {
      AddToRegisterMap(registerMap, "pc",
                       FormatRegister64Value(frame_arm64->context.iregs[32]));
    }
  } else if ((cpu == "mips") || (cpu == "mips64")) {
    using google_breakpad::StackFrameMIPS;
    const StackFrameMIPS* frame_mips =
        reinterpret_cast<const StackFrameMIPS*>(frame);

    if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_GP) {
      AddToRegisterMap(registerMap, "gp",
                       FormatRegister64Value(
                           frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_GP]));
    }
    if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_SP) {
      AddToRegisterMap(registerMap, "sp",
                       FormatRegister64Value(
                           frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_SP]));
    }
    if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_FP) {
      AddToRegisterMap(registerMap, "fp",
                       FormatRegister64Value(
                           frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_FP]));
    }
    if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_RA) {
      AddToRegisterMap(registerMap, "ra",
                       FormatRegister64Value(
                           frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_RA]));
    }
    if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_PC) {
      AddToRegisterMap(registerMap, "pc",
                       FormatRegister64Value(frame_mips->context.epc));
    }

    // Save registers s0-s7
    if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_S0) {
      AddToRegisterMap(registerMap, "s0",
                       FormatRegister64Value(
                           frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S0]));
    }
    if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_S1) {
      AddToRegisterMap(registerMap, "s1",
                       FormatRegister64Value(
                           frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S1]));
    }
    if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_S2) {
      AddToRegisterMap(registerMap, "s2",
                       FormatRegister64Value(
                           frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S2]));
    }
    if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_S3) {
      AddToRegisterMap(registerMap, "s3",
                       FormatRegister64Value(
                           frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S3]));
    }
    if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_S4) {
      AddToRegisterMap(registerMap, "s4",
                       FormatRegister64Value(
                           frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S4]));
    }
    if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_S5) {
      AddToRegisterMap(registerMap, "s5",
                       FormatRegister64Value(
                           frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S5]));
    }
    if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_S6) {
      AddToRegisterMap(registerMap, "s6",
                       FormatRegister64Value(
                           frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S6]));
    }
    if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_S7) {
      AddToRegisterMap(registerMap, "s7",
                       FormatRegister64Value(
                           frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S7]));
    }
  }
  return registerMap;
}

}  // namespace bugsnag_breakpad
