root/native_client_sdk/src/libraries/error_handling/error_handling.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. EHReadPointer
  2. EHPrintArch
  3. EHPrintSegments
  4. EHPrintFrame
  5. EHPrintMainContext
  6. EHGetTopFrame
  7. EHUnwindFrame
  8. EHStackInfoDestructor
  9. EHDefaultJsonHandler
  10. EHRequestExceptionsRaw
  11. EHRequestExceptionStackOnThread
  12. EHRequestExceptionsJson
  13. EHHanderInstalled

/* Copyright (c) 2013 The Chromium Authors. All rights reserved.
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file. */

#include <assert.h>
#include <inttypes.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>

#ifdef __GLIBC__
#include <elf.h>
#include <link.h>
#endif  /* __GLIBC__ */

#include "irt.h"
#include "nacl/nacl_exception.h"

#include "error_handling/error_handling.h"
#include "error_handling/string_stream.h"


#define PAGE_CHUNK_SIZE (64 * 1024)
#define PAGE_CHUNK_MASK (PAGE_CHUNK_SIZE - 1)
#define STACK_SIZE_MIN (PAGE_CHUNK_SIZE * 4)

#define MAX_FRAME_SIZE (10 * 1024)
#define MAX_FRAME_CAP 128

static pthread_key_t s_eh_stack_info_key;
static EHJsonHandler s_eh_json_callback = NULL;
static int s_eh_exception_enabled = 0;
static struct nacl_irt_exception_handling s_exception_handling;


typedef struct {
  void* stack;
  size_t size;
} EHStackInfo;

static uintptr_t EHReadPointer(uintptr_t offset) {
  return *((uintptr_t*) offset);
}

static void EHPrintArch(sstream_t* ss, struct NaClExceptionContext* context) {
#if defined(__x86_64__)
  ssprintf(ss, "\"arch\": \"x86_64\",\n");
#elif defined(__i386__)
  ssprintf(ss, "\"arch\": \"x86_32\",\n");
#elif defined(__arm__)
  ssprintf(ss, "\"arch\": \"arm\",\n");
#elif defined(__mips__)
  ssprintf(ss, "\"arch\": \"mips\",\n");
#else
#error Unknown ARCH
#endif
}

static void EHPrintSegments(sstream_t* ss,
                            struct NaClExceptionContext* context) {
  ssprintf(ss, "\"segments\": [");
  ssprintf(ss, "],\n");
}

static void EHPrintFrame(sstream_t* ss, EHFrame* frame) {
  uintptr_t start;
  uintptr_t i;

  ssprintf(ss, "{\n");
  ssprintf(ss, "\"frame_ptr\": %u,\n", frame->frame_ptr);
  ssprintf(ss, "\"prog_ctr\": %u,\n", frame->prog_ctr);
  ssprintf(ss, "\"data\": [\n");

#if defined(__x86_64__)
  start = frame->frame_ptr + 8;
#else
  start = frame->frame_ptr + 16;
#endif
  /* Capture the stack, no mare than 128 bytes to keep the size sane. */
  for (i = start; i < frame->next_ptr && i - start < MAX_FRAME_CAP; i += 4) {
    if (i != start) {
      ssprintf(ss, ",");
    }
    ssprintf(ss, "%u\n", EHReadPointer(i));
  }
  ssprintf(ss, "]\n}\n");
}


static void EHPrintMainContext(sstream_t* ss,
                               struct NaClExceptionContext* context) {
  struct NaClExceptionPortableContext* portable_context =
      nacl_exception_context_get_portable(context);
  ssprintf(ss, "\"handler\": {\n");
  ssprintf(ss, "\"prog_ctr\": %u,\n", portable_context->prog_ctr);
  ssprintf(ss, "\"stack_ptr\": %u,\n", portable_context->stack_ptr);
  ssprintf(ss, "\"frame_ptr\": %u\n", portable_context->frame_ptr);
  ssprintf(ss, "},\n");
}


int EHGetTopFrame(sstream_t* ss, struct NaClExceptionContext* context,
                  EHFrame* frame) {
  struct NaClExceptionPortableContext* portable_context =
      nacl_exception_context_get_portable(context);

  frame->prog_ctr = portable_context->prog_ctr;
  frame->frame_ptr = portable_context->frame_ptr;
  frame->next_ptr = EHReadPointer(portable_context->frame_ptr);
  return 1;
}


int EHUnwindFrame(EHFrame* frame) {
  uintptr_t frame_ptr;
  uintptr_t next;

  // Verify the current frame
  if (NULL == frame) return 0;

  frame_ptr = frame->frame_ptr;
  next = frame->next_ptr;

  // Abort if done or unwind moves us in the wrong direction
  if (next <= frame_ptr || next == 0) return 0;

  // Abort if frame is > 10K
  if (next - frame_ptr > MAX_FRAME_SIZE) return 0;

  // Unwind the frame
  frame->frame_ptr = next;
  frame->next_ptr = EHReadPointer(frame->frame_ptr);
#if defined(__x86_64__)
  frame->prog_ctr = EHReadPointer(frame_ptr + 8);
#else
  frame->prog_ctr = EHReadPointer(frame_ptr + 4);
#endif
  return 1;
}


static void EHStackInfoDestructor(void *arg) {
  EHStackInfo* info = (EHStackInfo*) arg;

  if (info) {
    munmap(info->stack, info->size);
  }
  free(info);
}


void EHDefaultJsonHandler(struct NaClExceptionContext* context) {
  if (s_eh_json_callback) {
    EHFrame frame;
    sstream_t ss;
    ssinit(&ss);

    ssprintf(&ss, "{\n");
    EHPrintArch(&ss, context);
    EHPrintSegments(&ss, context);
    EHPrintMainContext(&ss, context);

    EHGetTopFrame(&ss, context, &frame);
    int first = 1;

    ssprintf(&ss, "\"frames\": [\n");
    do {
      if (!first) ssprintf(&ss, ",");
      EHPrintFrame(&ss, &frame);
      first = 0;
    } while (EHUnwindFrame(&frame));

    /* End frame LIST and context DICT */
    ssprintf(&ss, "]\n}\n");
    s_eh_json_callback(ss.data);

    ssfree(&ss);
    while(1) sleep(9999);
  }
}


void EHRequestExceptionsRaw(EHRawHandler callback) {
  size_t interface_size = sizeof(s_exception_handling);
  if (s_eh_exception_enabled) {
    fprintf(stderr, "ERROR: EHInit already initialized.\n");
    return;
  }
  if (NULL == callback) {
    fprintf(stderr, "ERROR: EHInit called with NULL callback.\n");
    return;
  }

  /* Expect an exact match on the interface structure size. */
  if (nacl_interface_query(NACL_IRT_EXCEPTION_HANDLING_v0_1,
                           &s_exception_handling,
                           interface_size) != interface_size) {
    fprintf(stderr, "ERROR: EHInit failed nacl_interface_query\n");
    return;
  }

  if (s_exception_handling.exception_handler(callback, NULL) != 0) {
    fprintf(stderr, "ERROR: EHInit failed to install exception_handler\n");
    return;
  }

  s_eh_exception_enabled = 1;

  // Create a TLS key for storing per thread stack info
  pthread_key_create(&s_eh_stack_info_key, EHStackInfoDestructor);
}


void *EHRequestExceptionStackOnThread(size_t stack_size) {
  void* stack;
  void* guard;
  EHStackInfo* stack_info;

  // Set the stack size
  stack_size = (stack_size + PAGE_CHUNK_MASK) & PAGE_CHUNK_MASK;
  if (stack_size < STACK_SIZE_MIN) stack_size = STACK_SIZE_MIN;

  // Allocate stack + guard page
  stack = mmap(NULL, stack_size + PAGE_CHUNK_SIZE,
      PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
  if (MAP_FAILED == stack) return MAP_FAILED;

  // Remap to mprotect which may not be available
  guard = mmap(stack, PAGE_CHUNK_SIZE,
      PROT_NONE, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
  if (guard != stack) {
    fprintf(stderr, "WARNING: Failed to add guard page for alt stack.\n");
  }

  if (!s_exception_handling.exception_stack(stack, stack_size)) {
    fprintf(stderr, "ERROR: Failed to assign stack.\n");
    munmap(stack, stack_size);
    return MAP_FAILED;
  }

  // Allocate stack tracking information for this thread
  stack_info = (EHStackInfo*) malloc(sizeof(EHStackInfo));
  stack_info->stack = stack;
  stack_info->size = stack_size + PAGE_CHUNK_SIZE;
  pthread_setspecific(s_eh_stack_info_key, stack_info);
  return stack;
}


void EHRequestExceptionsJson(EHJsonHandler callback) {
  if (NULL == callback) return;

  EHRequestExceptionsRaw(EHDefaultJsonHandler);
  if (s_eh_exception_enabled) s_eh_json_callback = callback;
}


int EHHanderInstalled() {
  return s_eh_exception_enabled;
}

/* [<][>][^][v][top][bottom][index][help] */