// Copyright (c) 2010 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//     * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

// dump_context.cc: A (mini/micro)dump context.
//
// See dump_context.h for documentation.

#include "google_breakpad/processor/dump_context.h"

#include <assert.h>

#ifdef _WIN32
#include <io.h>
#else  // _WIN32
#include <unistd.h>
#endif  // _WIN32

#include "common/stdio_wrapper.h"
#include "processor/logging.h"

namespace google_breakpad {

DumpContext::DumpContext() : context_(),
                             context_flags_(0) { }

DumpContext::~DumpContext() {
  FreeContext();
}

uint32_t DumpContext::GetContextCPU() const {
  if (!valid_) {
    // Don't log a message, GetContextCPU can be legitimately called with
    // valid_ false by FreeContext, which is called by Read.
    return 0;
  }

  return context_flags_ & MD_CONTEXT_CPU_MASK;
}

uint32_t DumpContext::GetContextFlags() const {
  return context_flags_;
}

const MDRawContextX86* DumpContext::GetContextX86() const {
  if (GetContextCPU() != MD_CONTEXT_X86) {
    BPLOG(ERROR) << "DumpContext cannot get x86 context";
    return NULL;
  }

  return context_.x86;
}

const MDRawContextPPC* DumpContext::GetContextPPC() const {
  if (GetContextCPU() != MD_CONTEXT_PPC) {
    BPLOG(ERROR) << "DumpContext cannot get ppc context";
    return NULL;
  }

  return context_.ppc;
}

const MDRawContextPPC64* DumpContext::GetContextPPC64() const {
  if (GetContextCPU() != MD_CONTEXT_PPC64) {
    BPLOG(ERROR) << "DumpContext cannot get ppc64 context";
    return NULL;
  }

  return context_.ppc64;
}

const MDRawContextAMD64* DumpContext::GetContextAMD64() const {
  if (GetContextCPU() != MD_CONTEXT_AMD64) {
    BPLOG(ERROR) << "DumpContext cannot get amd64 context";
    return NULL;
  }

  return context_.amd64;
}

const MDRawContextSPARC* DumpContext::GetContextSPARC() const {
  if (GetContextCPU() != MD_CONTEXT_SPARC) {
    BPLOG(ERROR) << "DumpContext cannot get sparc context";
    return NULL;
  }

  return context_.ctx_sparc;
}

const MDRawContextARM* DumpContext::GetContextARM() const {
  if (GetContextCPU() != MD_CONTEXT_ARM) {
    BPLOG(ERROR) << "DumpContext cannot get arm context";
    return NULL;
  }

  return context_.arm;
}

const MDRawContextARM64* DumpContext::GetContextARM64() const {
  if (GetContextCPU() != MD_CONTEXT_ARM64) {
    BPLOG(ERROR) << "DumpContext cannot get arm64 context";
    return NULL;
  }

  return context_.arm64;
}

const MDRawContextMIPS* DumpContext::GetContextMIPS() const {
  if ((GetContextCPU() != MD_CONTEXT_MIPS) &&
      (GetContextCPU() != MD_CONTEXT_MIPS64)) {
    BPLOG(ERROR) << "DumpContext cannot get MIPS context";
    return NULL;
  }

  return context_.ctx_mips;
}

bool DumpContext::GetInstructionPointer(uint64_t* ip) const {
  BPLOG_IF(ERROR, !ip) << "DumpContext::GetInstructionPointer requires |ip|";
  assert(ip);
  *ip = 0;

  if (!valid_) {
    BPLOG(ERROR) << "Invalid DumpContext for GetInstructionPointer";
    return false;
  }

  switch (GetContextCPU()) {
  case MD_CONTEXT_AMD64:
    *ip = GetContextAMD64()->rip;
    break;
  case MD_CONTEXT_ARM:
    *ip = GetContextARM()->iregs[MD_CONTEXT_ARM_REG_PC];
    break;
  case MD_CONTEXT_ARM64:
    *ip = GetContextARM64()->iregs[MD_CONTEXT_ARM64_REG_PC];
    break;
  case MD_CONTEXT_PPC:
    *ip = GetContextPPC()->srr0;
    break;
  case MD_CONTEXT_PPC64:
    *ip = GetContextPPC64()->srr0;
    break;
  case MD_CONTEXT_SPARC:
    *ip = GetContextSPARC()->pc;
    break;
  case MD_CONTEXT_X86:
    *ip = GetContextX86()->eip;
    break;
  case MD_CONTEXT_MIPS:
  case MD_CONTEXT_MIPS64:
    *ip = GetContextMIPS()->epc;
    break;
  default:
    // This should never happen.
    BPLOG(ERROR) << "Unknown CPU architecture in GetInstructionPointer";
    return false;
  }
  return true;
}

bool DumpContext::GetStackPointer(uint64_t* sp) const {
  BPLOG_IF(ERROR, !sp) << "DumpContext::GetStackPointer requires |sp|";
  assert(sp);
  *sp = 0;

  if (!valid_) {
    BPLOG(ERROR) << "Invalid DumpContext for GetStackPointer";
    return false;
  }

  switch (GetContextCPU()) {
  case MD_CONTEXT_AMD64:
    *sp = GetContextAMD64()->rsp;
    break;
  case MD_CONTEXT_ARM:
    *sp = GetContextARM()->iregs[MD_CONTEXT_ARM_REG_SP];
    break;
  case MD_CONTEXT_ARM64:
    *sp = GetContextARM64()->iregs[MD_CONTEXT_ARM64_REG_SP];
    break;
  case MD_CONTEXT_PPC:
    *sp = GetContextPPC()->gpr[MD_CONTEXT_PPC_REG_SP];
    break;
  case MD_CONTEXT_PPC64:
    *sp = GetContextPPC64()->gpr[MD_CONTEXT_PPC64_REG_SP];
    break;
  case MD_CONTEXT_SPARC:
    *sp = GetContextSPARC()->g_r[MD_CONTEXT_SPARC_REG_SP];
    break;
  case MD_CONTEXT_X86:
    *sp = GetContextX86()->esp;
    break;
  case MD_CONTEXT_MIPS:
  case MD_CONTEXT_MIPS64:
    *sp = GetContextMIPS()->iregs[MD_CONTEXT_MIPS_REG_SP];
    break;
  default:
    // This should never happen.
    BPLOG(ERROR) << "Unknown CPU architecture in GetStackPointer";
    return false;
  }
  return true;
}

void DumpContext::SetContextFlags(uint32_t context_flags) {
  context_flags_ = context_flags;
}

void DumpContext::SetContextX86(MDRawContextX86* x86) {
  context_.x86 = x86;
}

void DumpContext::SetContextPPC(MDRawContextPPC* ppc) {
  context_.ppc = ppc;
}

void DumpContext::SetContextPPC64(MDRawContextPPC64* ppc64) {
  context_.ppc64 = ppc64;
}

void DumpContext::SetContextAMD64(MDRawContextAMD64* amd64) {
  context_.amd64 = amd64;
}

void DumpContext::SetContextSPARC(MDRawContextSPARC* ctx_sparc) {
  context_.ctx_sparc = ctx_sparc;
}

void DumpContext::SetContextARM(MDRawContextARM* arm) {
  context_.arm = arm;
}

void DumpContext::SetContextARM64(MDRawContextARM64* arm64) {
  context_.arm64 = arm64;
}

void DumpContext::SetContextMIPS(MDRawContextMIPS* ctx_mips) {
  context_.ctx_mips = ctx_mips;
}

void DumpContext::FreeContext() {
  switch (GetContextCPU()) {
    case MD_CONTEXT_X86:
      delete context_.x86;
      break;

    case MD_CONTEXT_PPC:
      delete context_.ppc;
      break;

    case MD_CONTEXT_PPC64:
      delete context_.ppc64;
      break;

    case MD_CONTEXT_AMD64:
      delete context_.amd64;
      break;

    case MD_CONTEXT_SPARC:
      delete context_.ctx_sparc;
      break;

    case MD_CONTEXT_ARM:
      delete context_.arm;
      break;

    case MD_CONTEXT_ARM64:
      delete context_.arm64;
      break;

    case MD_CONTEXT_MIPS:
    case MD_CONTEXT_MIPS64:
      delete context_.ctx_mips;
      break;

    default:
      // There is no context record (valid_ is false) or there's a
      // context record for an unknown CPU (shouldn't happen, only known
      // records are stored by Read).
      break;
  }

  context_flags_ = 0;
  context_.base = NULL;
}

void DumpContext::Print() {
  if (!valid_) {
    BPLOG(ERROR) << "DumpContext cannot print invalid data";
    return;
  }

  switch (GetContextCPU()) {
    case MD_CONTEXT_X86: {
      const MDRawContextX86* context_x86 = GetContextX86();
      printf("MDRawContextX86\n");
      printf("  context_flags                = 0x%x\n",
             context_x86->context_flags);
      printf("  dr0                          = 0x%x\n", context_x86->dr0);
      printf("  dr1                          = 0x%x\n", context_x86->dr1);
      printf("  dr2                          = 0x%x\n", context_x86->dr2);
      printf("  dr3                          = 0x%x\n", context_x86->dr3);
      printf("  dr6                          = 0x%x\n", context_x86->dr6);
      printf("  dr7                          = 0x%x\n", context_x86->dr7);
      printf("  float_save.control_word      = 0x%x\n",
             context_x86->float_save.control_word);
      printf("  float_save.status_word       = 0x%x\n",
             context_x86->float_save.status_word);
      printf("  float_save.tag_word          = 0x%x\n",
             context_x86->float_save.tag_word);
      printf("  float_save.error_offset      = 0x%x\n",
             context_x86->float_save.error_offset);
      printf("  float_save.error_selector    = 0x%x\n",
             context_x86->float_save.error_selector);
      printf("  float_save.data_offset       = 0x%x\n",
             context_x86->float_save.data_offset);
      printf("  float_save.data_selector     = 0x%x\n",
             context_x86->float_save.data_selector);
      printf("  float_save.register_area[%2d] = 0x",
             MD_FLOATINGSAVEAREA_X86_REGISTERAREA_SIZE);
      for (unsigned int register_index = 0;
           register_index < MD_FLOATINGSAVEAREA_X86_REGISTERAREA_SIZE;
           ++register_index) {
        printf("%02x", context_x86->float_save.register_area[register_index]);
      }
      printf("\n");
      printf("  float_save.cr0_npx_state     = 0x%x\n",
             context_x86->float_save.cr0_npx_state);
      printf("  gs                           = 0x%x\n", context_x86->gs);
      printf("  fs                           = 0x%x\n", context_x86->fs);
      printf("  es                           = 0x%x\n", context_x86->es);
      printf("  ds                           = 0x%x\n", context_x86->ds);
      printf("  edi                          = 0x%x\n", context_x86->edi);
      printf("  esi                          = 0x%x\n", context_x86->esi);
      printf("  ebx                          = 0x%x\n", context_x86->ebx);
      printf("  edx                          = 0x%x\n", context_x86->edx);
      printf("  ecx                          = 0x%x\n", context_x86->ecx);
      printf("  eax                          = 0x%x\n", context_x86->eax);
      printf("  ebp                          = 0x%x\n", context_x86->ebp);
      printf("  eip                          = 0x%x\n", context_x86->eip);
      printf("  cs                           = 0x%x\n", context_x86->cs);
      printf("  eflags                       = 0x%x\n", context_x86->eflags);
      printf("  esp                          = 0x%x\n", context_x86->esp);
      printf("  ss                           = 0x%x\n", context_x86->ss);
      printf("  extended_registers[%3d]      = 0x",
             MD_CONTEXT_X86_EXTENDED_REGISTERS_SIZE);
      for (unsigned int register_index = 0;
           register_index < MD_CONTEXT_X86_EXTENDED_REGISTERS_SIZE;
           ++register_index) {
        printf("%02x", context_x86->extended_registers[register_index]);
      }
      printf("\n\n");

      break;
    }

    case MD_CONTEXT_PPC: {
      const MDRawContextPPC* context_ppc = GetContextPPC();
      printf("MDRawContextPPC\n");
      printf("  context_flags            = 0x%x\n",
             context_ppc->context_flags);
      printf("  srr0                     = 0x%x\n", context_ppc->srr0);
      printf("  srr1                     = 0x%x\n", context_ppc->srr1);
      for (unsigned int gpr_index = 0;
           gpr_index < MD_CONTEXT_PPC_GPR_COUNT;
           ++gpr_index) {
        printf("  gpr[%2d]                  = 0x%x\n",
               gpr_index, context_ppc->gpr[gpr_index]);
      }
      printf("  cr                       = 0x%x\n", context_ppc->cr);
      printf("  xer                      = 0x%x\n", context_ppc->xer);
      printf("  lr                       = 0x%x\n", context_ppc->lr);
      printf("  ctr                      = 0x%x\n", context_ppc->ctr);
      printf("  mq                       = 0x%x\n", context_ppc->mq);
      printf("  vrsave                   = 0x%x\n", context_ppc->vrsave);
      for (unsigned int fpr_index = 0;
           fpr_index < MD_FLOATINGSAVEAREA_PPC_FPR_COUNT;
           ++fpr_index) {
        printf("  float_save.fpregs[%2d]    = 0x%" PRIx64 "\n",
               fpr_index, context_ppc->float_save.fpregs[fpr_index]);
      }
      printf("  float_save.fpscr         = 0x%x\n",
             context_ppc->float_save.fpscr);
      // TODO(mmentovai): print the 128-bit quantities in
      // context_ppc->vector_save.  This isn't done yet because printf
      // doesn't support 128-bit quantities, and printing them using
      // PRIx64 as two 64-bit quantities requires knowledge of the CPU's
      // byte ordering.
      printf("  vector_save.save_vrvalid = 0x%x\n",
             context_ppc->vector_save.save_vrvalid);
      printf("\n");

      break;
    }

    case MD_CONTEXT_PPC64: {
      const MDRawContextPPC64* context_ppc64 = GetContextPPC64();
      printf("MDRawContextPPC64\n");
      printf("  context_flags            = 0x%" PRIx64 "\n",
             context_ppc64->context_flags);
      printf("  srr0                     = 0x%" PRIx64 "\n",
             context_ppc64->srr0);
      printf("  srr1                     = 0x%" PRIx64 "\n",
             context_ppc64->srr1);
      for (unsigned int gpr_index = 0;
           gpr_index < MD_CONTEXT_PPC64_GPR_COUNT;
           ++gpr_index) {
        printf("  gpr[%2d]                  = 0x%" PRIx64 "\n",
               gpr_index, context_ppc64->gpr[gpr_index]);
      }
      printf("  cr                       = 0x%" PRIx64 "\n", context_ppc64->cr);
      printf("  xer                      = 0x%" PRIx64 "\n",
             context_ppc64->xer);
      printf("  lr                       = 0x%" PRIx64 "\n", context_ppc64->lr);
      printf("  ctr                      = 0x%" PRIx64 "\n",
             context_ppc64->ctr);
      printf("  vrsave                   = 0x%" PRIx64 "\n",
             context_ppc64->vrsave);
      for (unsigned int fpr_index = 0;
           fpr_index < MD_FLOATINGSAVEAREA_PPC_FPR_COUNT;
           ++fpr_index) {
        printf("  float_save.fpregs[%2d]    = 0x%" PRIx64 "\n",
               fpr_index, context_ppc64->float_save.fpregs[fpr_index]);
      }
      printf("  float_save.fpscr         = 0x%x\n",
             context_ppc64->float_save.fpscr);
      // TODO(mmentovai): print the 128-bit quantities in
      // context_ppc64->vector_save.  This isn't done yet because printf
      // doesn't support 128-bit quantities, and printing them using
      // PRIx64 as two 64-bit quantities requires knowledge of the CPU's
      // byte ordering.
      printf("  vector_save.save_vrvalid = 0x%x\n",
             context_ppc64->vector_save.save_vrvalid);
      printf("\n");

      break;
    }

    case MD_CONTEXT_AMD64: {
      const MDRawContextAMD64* context_amd64 = GetContextAMD64();
      printf("MDRawContextAMD64\n");
      printf("  p1_home       = 0x%" PRIx64 "\n",
             context_amd64->p1_home);
      printf("  p2_home       = 0x%" PRIx64 "\n",
             context_amd64->p2_home);
      printf("  p3_home       = 0x%" PRIx64 "\n",
             context_amd64->p3_home);
      printf("  p4_home       = 0x%" PRIx64 "\n",
             context_amd64->p4_home);
      printf("  p5_home       = 0x%" PRIx64 "\n",
             context_amd64->p5_home);
      printf("  p6_home       = 0x%" PRIx64 "\n",
             context_amd64->p6_home);
      printf("  context_flags = 0x%x\n",
             context_amd64->context_flags);
      printf("  mx_csr        = 0x%x\n",
             context_amd64->mx_csr);
      printf("  cs            = 0x%x\n", context_amd64->cs);
      printf("  ds            = 0x%x\n", context_amd64->ds);
      printf("  es            = 0x%x\n", context_amd64->es);
      printf("  fs            = 0x%x\n", context_amd64->fs);
      printf("  gs            = 0x%x\n", context_amd64->gs);
      printf("  ss            = 0x%x\n", context_amd64->ss);
      printf("  eflags        = 0x%x\n", context_amd64->eflags);
      printf("  dr0           = 0x%" PRIx64 "\n", context_amd64->dr0);
      printf("  dr1           = 0x%" PRIx64 "\n", context_amd64->dr1);
      printf("  dr2           = 0x%" PRIx64 "\n", context_amd64->dr2);
      printf("  dr3           = 0x%" PRIx64 "\n", context_amd64->dr3);
      printf("  dr6           = 0x%" PRIx64 "\n", context_amd64->dr6);
      printf("  dr7           = 0x%" PRIx64 "\n", context_amd64->dr7);
      printf("  rax           = 0x%" PRIx64 "\n", context_amd64->rax);
      printf("  rcx           = 0x%" PRIx64 "\n", context_amd64->rcx);
      printf("  rdx           = 0x%" PRIx64 "\n", context_amd64->rdx);
      printf("  rbx           = 0x%" PRIx64 "\n", context_amd64->rbx);
      printf("  rsp           = 0x%" PRIx64 "\n", context_amd64->rsp);
      printf("  rbp           = 0x%" PRIx64 "\n", context_amd64->rbp);
      printf("  rsi           = 0x%" PRIx64 "\n", context_amd64->rsi);
      printf("  rdi           = 0x%" PRIx64 "\n", context_amd64->rdi);
      printf("  r8            = 0x%" PRIx64 "\n", context_amd64->r8);
      printf("  r9            = 0x%" PRIx64 "\n", context_amd64->r9);
      printf("  r10           = 0x%" PRIx64 "\n", context_amd64->r10);
      printf("  r11           = 0x%" PRIx64 "\n", context_amd64->r11);
      printf("  r12           = 0x%" PRIx64 "\n", context_amd64->r12);
      printf("  r13           = 0x%" PRIx64 "\n", context_amd64->r13);
      printf("  r14           = 0x%" PRIx64 "\n", context_amd64->r14);
      printf("  r15           = 0x%" PRIx64 "\n", context_amd64->r15);
      printf("  rip           = 0x%" PRIx64 "\n", context_amd64->rip);
      // TODO: print xmm, vector, debug registers
      printf("\n");
      break;
    }

    case MD_CONTEXT_SPARC: {
      const MDRawContextSPARC* context_sparc = GetContextSPARC();
      printf("MDRawContextSPARC\n");
      printf("  context_flags       = 0x%x\n",
             context_sparc->context_flags);
      for (unsigned int g_r_index = 0;
           g_r_index < MD_CONTEXT_SPARC_GPR_COUNT;
           ++g_r_index) {
        printf("  g_r[%2d]             = 0x%" PRIx64 "\n",
               g_r_index, context_sparc->g_r[g_r_index]);
      }
      printf("  ccr                 = 0x%" PRIx64 "\n", context_sparc->ccr);
      printf("  pc                  = 0x%" PRIx64 "\n", context_sparc->pc);
      printf("  npc                 = 0x%" PRIx64 "\n", context_sparc->npc);
      printf("  y                   = 0x%" PRIx64 "\n", context_sparc->y);
      printf("  asi                 = 0x%" PRIx64 "\n", context_sparc->asi);
      printf("  fprs                = 0x%" PRIx64 "\n", context_sparc->fprs);

      for (unsigned int fpr_index = 0;
           fpr_index < MD_FLOATINGSAVEAREA_SPARC_FPR_COUNT;
           ++fpr_index) {
        printf("  float_save.regs[%2d] = 0x%" PRIx64 "\n",
               fpr_index, context_sparc->float_save.regs[fpr_index]);
      }
      printf("  float_save.filler   = 0x%" PRIx64 "\n",
             context_sparc->float_save.filler);
      printf("  float_save.fsr      = 0x%" PRIx64 "\n",
             context_sparc->float_save.fsr);
      break;
    }

    case MD_CONTEXT_ARM: {
      const MDRawContextARM* context_arm = GetContextARM();
      printf("MDRawContextARM\n");
      printf("  context_flags       = 0x%x\n",
             context_arm->context_flags);
      for (unsigned int ireg_index = 0;
           ireg_index < MD_CONTEXT_ARM_GPR_COUNT;
           ++ireg_index) {
        printf("  iregs[%2d]            = 0x%x\n",
               ireg_index, context_arm->iregs[ireg_index]);
      }
      printf("  cpsr                = 0x%x\n", context_arm->cpsr);
      printf("  float_save.fpscr     = 0x%" PRIx64 "\n",
             context_arm->float_save.fpscr);
      for (unsigned int fpr_index = 0;
           fpr_index < MD_FLOATINGSAVEAREA_ARM_FPR_COUNT;
           ++fpr_index) {
        printf("  float_save.regs[%2d] = 0x%" PRIx64 "\n",
               fpr_index, context_arm->float_save.regs[fpr_index]);
      }
      for (unsigned int fpe_index = 0;
           fpe_index < MD_FLOATINGSAVEAREA_ARM_FPEXTRA_COUNT;
           ++fpe_index) {
        printf("  float_save.extra[%2d] = 0x%" PRIx32 "\n",
               fpe_index, context_arm->float_save.extra[fpe_index]);
      }

      break;
    }

    case MD_CONTEXT_ARM64: {
      const MDRawContextARM64* context_arm64 = GetContextARM64();
      printf("MDRawContextARM64\n");
      printf("  context_flags       = 0x%" PRIx64 "\n",
             context_arm64->context_flags);
      for (unsigned int ireg_index = 0;
           ireg_index < MD_CONTEXT_ARM64_GPR_COUNT;
           ++ireg_index) {
        printf("  iregs[%2d]            = 0x%" PRIx64 "\n",
               ireg_index, context_arm64->iregs[ireg_index]);
      }
      printf("  cpsr                = 0x%x\n", context_arm64->cpsr);
      printf("  float_save.fpsr     = 0x%x\n", context_arm64->float_save.fpsr);
      printf("  float_save.fpcr     = 0x%x\n", context_arm64->float_save.fpcr);

      for (unsigned int freg_index = 0;
           freg_index < MD_FLOATINGSAVEAREA_ARM64_FPR_COUNT;
           ++freg_index) {
        uint128_struct fp_value = context_arm64->float_save.regs[freg_index];
        printf("  float_save.regs[%2d]            = 0x%" PRIx64 "%" PRIx64 "\n",
               freg_index, fp_value.high, fp_value.low);
      }
      break;
    }

    case MD_CONTEXT_MIPS:
    case MD_CONTEXT_MIPS64: {
      const MDRawContextMIPS* context_mips = GetContextMIPS();
      printf("MDRawContextMIPS\n");
      printf("  context_flags        = 0x%x\n",
             context_mips->context_flags);
      for (int ireg_index = 0;
           ireg_index < MD_CONTEXT_MIPS_GPR_COUNT;
           ++ireg_index) {
        printf("  iregs[%2d]           = 0x%" PRIx64 "\n",
               ireg_index, context_mips->iregs[ireg_index]);
      }
      printf("  mdhi                 = 0x%" PRIx64 "\n",
             context_mips->mdhi);
      printf("  mdlo                 = 0x%" PRIx64 "\n",
             context_mips->mdhi);
      for (int dsp_index = 0;
           dsp_index < MD_CONTEXT_MIPS_DSP_COUNT;
           ++dsp_index) {
        printf("  hi[%1d]              = 0x%" PRIx32 "\n",
               dsp_index, context_mips->hi[dsp_index]);
        printf("  lo[%1d]              = 0x%" PRIx32 "\n",
               dsp_index, context_mips->lo[dsp_index]);
      }
      printf("  dsp_control          = 0x%" PRIx32 "\n",
             context_mips->dsp_control);
      printf("  epc                  = 0x%" PRIx64 "\n",
             context_mips->epc);
      printf("  badvaddr             = 0x%" PRIx64 "\n",
             context_mips->badvaddr);
      printf("  status               = 0x%" PRIx32 "\n",
             context_mips->status);
      printf("  cause                = 0x%" PRIx32 "\n",
             context_mips->cause);

      for (int fpr_index = 0;
           fpr_index < MD_FLOATINGSAVEAREA_MIPS_FPR_COUNT;
           ++fpr_index) {
        printf("  float_save.regs[%2d] = 0x%" PRIx64 "\n",
               fpr_index, context_mips->float_save.regs[fpr_index]);
      }
      printf("  float_save.fpcsr     = 0x%" PRIx32 "\n",
             context_mips->float_save.fpcsr);
      printf("  float_save.fir       = 0x%" PRIx32 "\n",
             context_mips->float_save.fir);
      break;
    }

    default: {
      break;
    }
  }
}

}  // namespace google_breakpad
