Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / native_client_sdk / src / libraries / error_handling / error_handling.c
bloba3749da12e7d60ddf6850dbc3a8579710f446fde
1 /* Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 * Use of this source code is governed by a BSD-style license that can be
3 * found in the LICENSE file. */
5 #include <assert.h>
6 #include <inttypes.h>
7 #include <pthread.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <sys/mman.h>
12 #include <unistd.h>
14 #ifdef __GLIBC__
15 #include <elf.h>
16 #include <link.h>
17 #endif /* __GLIBC__ */
19 #include "irt.h"
20 #include "nacl/nacl_exception.h"
22 #include "error_handling/error_handling.h"
23 #include "error_handling/string_stream.h"
26 #define PAGE_CHUNK_SIZE (64 * 1024)
27 #define PAGE_CHUNK_MASK (PAGE_CHUNK_SIZE - 1)
28 #define STACK_SIZE_MIN (PAGE_CHUNK_SIZE * 4)
30 #define MAX_FRAME_SIZE (10 * 1024)
31 #define MAX_FRAME_CAP 128
33 static pthread_key_t s_eh_stack_info_key;
34 static EHJsonHandler s_eh_json_callback = NULL;
35 static int s_eh_exception_enabled = 0;
36 static struct nacl_irt_exception_handling s_exception_handling;
39 typedef struct {
40 void* stack;
41 size_t size;
42 } EHStackInfo;
44 static uintptr_t EHReadPointer(uintptr_t offset) {
45 return *((uintptr_t*) offset);
48 static void EHPrintArch(sstream_t* ss, struct NaClExceptionContext* context) {
49 #if defined(__x86_64__)
50 ssprintf(ss, "\"arch\": \"x86_64\",\n");
51 #elif defined(__i386__)
52 ssprintf(ss, "\"arch\": \"x86_32\",\n");
53 #elif defined(__arm__)
54 ssprintf(ss, "\"arch\": \"arm\",\n");
55 #elif defined(__mips__)
56 ssprintf(ss, "\"arch\": \"mips\",\n");
57 #else
58 #error Unknown ARCH
59 #endif
62 static void EHPrintSegments(sstream_t* ss,
63 struct NaClExceptionContext* context) {
64 ssprintf(ss, "\"segments\": [");
65 ssprintf(ss, "],\n");
68 static void EHPrintFrame(sstream_t* ss, EHFrame* frame) {
69 uintptr_t start;
70 uintptr_t i;
72 ssprintf(ss, "{\n");
73 ssprintf(ss, "\"frame_ptr\": %u,\n", frame->frame_ptr);
74 ssprintf(ss, "\"prog_ctr\": %u,\n", frame->prog_ctr);
75 ssprintf(ss, "\"data\": [\n");
77 #if defined(__x86_64__)
78 start = frame->frame_ptr + 8;
79 #else
80 start = frame->frame_ptr + 16;
81 #endif
82 /* Capture the stack, no mare than 128 bytes to keep the size sane. */
83 for (i = start; i < frame->next_ptr && i - start < MAX_FRAME_CAP; i += 4) {
84 if (i != start) {
85 ssprintf(ss, ",");
87 ssprintf(ss, "%u\n", EHReadPointer(i));
89 ssprintf(ss, "]\n}\n");
93 static void EHPrintMainContext(sstream_t* ss,
94 struct NaClExceptionContext* context) {
95 struct NaClExceptionPortableContext* portable_context =
96 nacl_exception_context_get_portable(context);
97 ssprintf(ss, "\"handler\": {\n");
98 ssprintf(ss, "\"prog_ctr\": %u,\n", portable_context->prog_ctr);
99 ssprintf(ss, "\"stack_ptr\": %u,\n", portable_context->stack_ptr);
100 ssprintf(ss, "\"frame_ptr\": %u\n", portable_context->frame_ptr);
101 ssprintf(ss, "},\n");
105 int EHGetTopFrame(sstream_t* ss, struct NaClExceptionContext* context,
106 EHFrame* frame) {
107 struct NaClExceptionPortableContext* portable_context =
108 nacl_exception_context_get_portable(context);
110 frame->prog_ctr = portable_context->prog_ctr;
111 frame->frame_ptr = portable_context->frame_ptr;
112 frame->next_ptr = EHReadPointer(portable_context->frame_ptr);
113 return 1;
117 int EHUnwindFrame(EHFrame* frame) {
118 uintptr_t frame_ptr;
119 uintptr_t next;
121 // Verify the current frame
122 if (NULL == frame) return 0;
124 frame_ptr = frame->frame_ptr;
125 next = frame->next_ptr;
127 // Abort if done or unwind moves us in the wrong direction
128 if (next <= frame_ptr || next == 0) return 0;
130 // Abort if frame is > 10K
131 if (next - frame_ptr > MAX_FRAME_SIZE) return 0;
133 // Unwind the frame
134 frame->frame_ptr = next;
135 frame->next_ptr = EHReadPointer(frame->frame_ptr);
136 #if defined(__x86_64__)
137 frame->prog_ctr = EHReadPointer(frame_ptr + 8);
138 #else
139 frame->prog_ctr = EHReadPointer(frame_ptr + 4);
140 #endif
141 return 1;
145 static void EHStackInfoDestructor(void *arg) {
146 EHStackInfo* info = (EHStackInfo*) arg;
148 if (info) {
149 munmap(info->stack, info->size);
151 free(info);
155 void EHDefaultJsonHandler(struct NaClExceptionContext* context) {
156 if (s_eh_json_callback) {
157 EHFrame frame;
158 sstream_t ss;
159 ssinit(&ss);
161 ssprintf(&ss, "{\n");
162 EHPrintArch(&ss, context);
163 EHPrintSegments(&ss, context);
164 EHPrintMainContext(&ss, context);
166 EHGetTopFrame(&ss, context, &frame);
167 int first = 1;
169 ssprintf(&ss, "\"frames\": [\n");
170 do {
171 if (!first) ssprintf(&ss, ",");
172 EHPrintFrame(&ss, &frame);
173 first = 0;
174 } while (EHUnwindFrame(&frame));
176 /* End frame LIST and context DICT */
177 ssprintf(&ss, "]\n}\n");
178 s_eh_json_callback(ss.data);
180 ssfree(&ss);
181 while(1) sleep(9999);
186 void EHRequestExceptionsRaw(EHRawHandler callback) {
187 size_t interface_size = sizeof(s_exception_handling);
188 if (s_eh_exception_enabled) {
189 fprintf(stderr, "ERROR: EHInit already initialized.\n");
190 return;
192 if (NULL == callback) {
193 fprintf(stderr, "ERROR: EHInit called with NULL callback.\n");
194 return;
197 /* Expect an exact match on the interface structure size. */
198 if (nacl_interface_query(NACL_IRT_EXCEPTION_HANDLING_v0_1,
199 &s_exception_handling,
200 interface_size) != interface_size) {
201 fprintf(stderr, "ERROR: EHInit failed nacl_interface_query\n");
202 return;
205 if (s_exception_handling.exception_handler(callback, NULL) != 0) {
206 fprintf(stderr, "ERROR: EHInit failed to install exception_handler\n");
207 return;
210 s_eh_exception_enabled = 1;
212 // Create a TLS key for storing per thread stack info
213 pthread_key_create(&s_eh_stack_info_key, EHStackInfoDestructor);
217 void *EHRequestExceptionStackOnThread(size_t stack_size) {
218 void* stack;
219 void* guard;
220 EHStackInfo* stack_info;
222 // Set the stack size
223 stack_size = (stack_size + PAGE_CHUNK_MASK) & PAGE_CHUNK_MASK;
224 if (stack_size < STACK_SIZE_MIN) stack_size = STACK_SIZE_MIN;
226 // Allocate stack + guard page
227 stack = mmap(NULL, stack_size + PAGE_CHUNK_SIZE,
228 PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
229 if (MAP_FAILED == stack) return MAP_FAILED;
231 // Remap to mprotect which may not be available
232 guard = mmap(stack, PAGE_CHUNK_SIZE,
233 PROT_NONE, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
234 if (guard != stack) {
235 fprintf(stderr, "WARNING: Failed to add guard page for alt stack.\n");
238 if (!s_exception_handling.exception_stack(stack, stack_size)) {
239 fprintf(stderr, "ERROR: Failed to assign stack.\n");
240 munmap(stack, stack_size);
241 return MAP_FAILED;
244 // Allocate stack tracking information for this thread
245 stack_info = (EHStackInfo*) malloc(sizeof(EHStackInfo));
246 stack_info->stack = stack;
247 stack_info->size = stack_size + PAGE_CHUNK_SIZE;
248 pthread_setspecific(s_eh_stack_info_key, stack_info);
249 return stack;
253 void EHRequestExceptionsJson(EHJsonHandler callback) {
254 if (NULL == callback) return;
256 EHRequestExceptionsRaw(EHDefaultJsonHandler);
257 if (s_eh_exception_enabled) s_eh_json_callback = callback;
261 int EHHanderInstalled() {
262 return s_eh_exception_enabled;