Copyright 2012 -> 2013
[folly.git] / folly / experimental / exception_tracer / StackTrace.c
1 /*
2  * Copyright 2013 Facebook, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17
18 #include "folly/experimental/exception_tracer/StackTrace.h"
19
20 #include <errno.h>
21 #include <stdlib.h>
22 #include "unwind.h"
23
24 struct Context {
25   StackTrace* trace;
26   size_t skip;
27   size_t capacity;
28 };
29
30 static _Unwind_Reason_Code addIP(struct _Unwind_Context* ctx, void* varg) {
31   struct Context* arg = (struct Context*)varg;
32
33   if (arg->skip) {
34     --arg->skip;
35     return _URC_NO_REASON;
36   }
37
38   if (arg->trace->frameCount == arg->capacity) {
39     size_t newCapacity = (arg->capacity < 8 ? 8 : arg->capacity * 1.5);
40     uintptr_t* newBlock =
41       realloc(arg->trace->frameIPs, newCapacity * sizeof(uintptr_t));
42     if (!newBlock) {
43       return _URC_FATAL_PHASE1_ERROR;
44     }
45     arg->trace->frameIPs = newBlock;
46     arg->capacity = newCapacity;
47   }
48
49   arg->trace->frameIPs[arg->trace->frameCount++] = _Unwind_GetIP(ctx);
50   return _URC_NO_REASON;  /* success */
51 }
52
53 int getCurrentStackTrace(size_t skip, StackTrace* trace) {
54   trace->frameIPs = NULL;
55   trace->frameCount = 0;
56   struct Context ctx;
57   ctx.trace = trace;
58   ctx.skip = skip;
59   ctx.capacity = 0;
60
61   if (_Unwind_Backtrace(addIP, &ctx) == _URC_END_OF_STACK) {
62     return 0;
63   }
64
65   destroyStackTrace(trace);
66   return -ENOMEM;
67 }
68
69 void destroyStackTrace(StackTrace* trace) {
70   free(trace->frameIPs);
71   trace->frameIPs = NULL;
72   trace->frameCount = 0;
73 }
74
75 int pushCurrentStackTrace(size_t skip, StackTraceStack** head) {
76   StackTraceStack* newHead = malloc(sizeof(StackTraceStack));
77   if (!newHead) {
78     return -ENOMEM;
79   }
80
81   int err;
82   if ((err = getCurrentStackTrace(skip, &newHead->trace)) != 0) {
83     free(newHead);
84     return -ENOMEM;
85   }
86
87   newHead->next = *head;
88   *head = newHead;
89   return 0;
90 }
91
92 void popStackTrace(StackTraceStack** head) {
93   StackTraceStack* oldHead = *head;
94   *head = oldHead->next;
95   destroyStackTrace(&oldHead->trace);
96   free(oldHead);
97 }
98
99 void clearStack(StackTraceStack** head) {
100   while (*head) {
101     popStackTrace(head);
102   }
103 }
104
105 int moveTop(StackTraceStack** from, StackTraceStack** to) {
106   StackTraceStack* top = *from;
107   if (!top) {
108     return -EINVAL;
109   }
110
111   *from = top->next;
112   top->next = *to;
113   *to = top;
114   return 0;
115 }
116