@arms/rum-reactnative 0.1.2 → 0.1.3-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (172) hide show
  1. package/README.md +79 -1
  2. package/RumSdkReactnative.podspec +28 -0
  3. package/android/build.gradle +83 -0
  4. package/android/gradle.properties +6 -0
  5. package/android/src/main/AndroidManifest.xml +13 -0
  6. package/android/src/main/java/com/rumsdkreactnative/RumSdkReactnativeModule.kt +512 -0
  7. package/android/src/main/java/com/rumsdkreactnative/RumSdkReactnativePackage.kt +33 -0
  8. package/android/src/main/java/com/rumsdkreactnative/crash/CrashContextManager.kt +157 -0
  9. package/android/src/main/java/com/rumsdkreactnative/crash/CrashHandler.kt +444 -0
  10. package/android/src/main/java/com/rumsdkreactnative/crash/CrashInitProvider.kt +50 -0
  11. package/android/src/main/java/com/rumsdkreactnative/crash/NdkCrashHandler.kt +66 -0
  12. package/android/src/main/jni/CMakeLists.txt +21 -0
  13. package/android/src/main/jni/rum_backtrace.c +144 -0
  14. package/android/src/main/jni/rum_crash_handler.c +200 -0
  15. package/android/src/main/jni/rum_crash_writer.c +436 -0
  16. package/android/src/main/jni/rum_module_info.c +355 -0
  17. package/android/src/main/jni/rum_module_info.h +58 -0
  18. package/android/src/main/jni/rum_test_crash.cpp +55 -0
  19. package/app.plugin.js +11 -0
  20. package/docs/ReactNative SDK/351/205/215/347/275/256/345/217/202/350/200/203.md" +671 -0
  21. package/docs/SDK /347/211/210/346/234/254/350/257/264/346/230/216.md" +12 -0
  22. package/docs//346/216/245/345/205/245ReactNative/345/272/224/347/224/250.md +238 -0
  23. package/ios/PrivacyInfo.xcprivacy +93 -0
  24. package/ios/RumCrashReporter.h +38 -0
  25. package/ios/RumCrashReporter.mm +861 -0
  26. package/ios/RumSdkReactnative.h +5 -0
  27. package/ios/RumSdkReactnative.mm +291 -0
  28. package/ios/fishhook/fishhook.c +264 -0
  29. package/ios/fishhook/fishhook.h +76 -0
  30. package/lib/commonjs/NativeRumSdkReactnative.js +50 -0
  31. package/lib/commonjs/collector/action/index.js +108 -0
  32. package/lib/commonjs/collector/exception/crash.js +148 -0
  33. package/lib/commonjs/collector/exception/index.js +238 -0
  34. package/lib/commonjs/collector/index.js +42 -0
  35. package/lib/commonjs/collector/resource/api.js +286 -0
  36. package/lib/commonjs/collector/view/perf.js +79 -0
  37. package/lib/commonjs/collector/view/pv.js +88 -0
  38. package/lib/commonjs/expoConfigPlugin.js +37 -0
  39. package/lib/commonjs/index.js +29 -0
  40. package/lib/commonjs/package.json +1 -0
  41. package/lib/commonjs/plugin.js +47 -0
  42. package/lib/commonjs/processor/default-processor.js +27 -0
  43. package/lib/commonjs/processor/session-processor.js +47 -0
  44. package/lib/commonjs/reporter/index.js +148 -0
  45. package/lib/commonjs/reporter/monitor-reporter.js +43 -0
  46. package/lib/commonjs/shell.js +91 -0
  47. package/lib/commonjs/types/client.js +6 -0
  48. package/lib/commonjs/types/crash.js +37 -0
  49. package/lib/commonjs/types/index.js +2 -0
  50. package/lib/commonjs/utils/action.js +305 -0
  51. package/lib/commonjs/utils/api.js +83 -0
  52. package/lib/commonjs/utils/base.js +95 -0
  53. package/lib/commonjs/utils/crash.js +149 -0
  54. package/lib/commonjs/utils/platform.js +162 -0
  55. package/lib/commonjs/utils/session.js +158 -0
  56. package/lib/commonjs/utils/url.js +59 -0
  57. package/lib/commonjs/utils/view.js +93 -0
  58. package/lib/module/NativeRumSdkReactnative.js +47 -0
  59. package/lib/module/collector/action/index.js +45 -10
  60. package/lib/module/collector/exception/crash.js +143 -0
  61. package/lib/module/collector/exception/index.js +144 -46
  62. package/lib/module/collector/index.js +8 -0
  63. package/lib/module/collector/resource/api.js +6 -6
  64. package/lib/module/collector/view/pv.js +11 -0
  65. package/lib/module/expoConfigPlugin.js +33 -0
  66. package/lib/module/index.js +1 -0
  67. package/lib/module/processor/session-processor.js +22 -0
  68. package/lib/module/reporter/index.js +102 -16
  69. package/lib/module/reporter/monitor-reporter.js +38 -0
  70. package/lib/module/shell.js +11 -7
  71. package/lib/module/types/crash.js +33 -0
  72. package/lib/module/utils/action.js +299 -0
  73. package/lib/module/utils/crash.js +144 -0
  74. package/lib/module/utils/platform.js +124 -72
  75. package/lib/module/utils/session.js +23 -4
  76. package/lib/typescript/commonjs/package.json +1 -0
  77. package/lib/typescript/commonjs/src/NativeRumSdkReactnative.d.ts +49 -0
  78. package/lib/typescript/{src → commonjs/src}/collector/action/index.d.ts +6 -0
  79. package/lib/typescript/commonjs/src/collector/exception/crash.d.ts +28 -0
  80. package/lib/typescript/{src → commonjs/src}/collector/exception/index.d.ts +7 -0
  81. package/lib/typescript/commonjs/src/collector/index.d.ts +6 -0
  82. package/lib/typescript/commonjs/src/expoConfigPlugin.d.ts +28 -0
  83. package/lib/typescript/commonjs/src/index.d.ts +6 -0
  84. package/lib/typescript/{src → commonjs/src}/processor/session-processor.d.ts +2 -0
  85. package/lib/typescript/{src → commonjs/src}/reporter/index.d.ts +3 -1
  86. package/lib/typescript/commonjs/src/reporter/monitor-reporter.d.ts +8 -0
  87. package/lib/typescript/{src → commonjs/src}/shell.d.ts +11 -0
  88. package/lib/typescript/commonjs/src/types/crash.d.ts +95 -0
  89. package/lib/typescript/commonjs/src/utils/action.d.ts +63 -0
  90. package/lib/typescript/commonjs/src/utils/crash.d.ts +12 -0
  91. package/lib/typescript/commonjs/src/utils/platform.d.ts +22 -0
  92. package/lib/typescript/{src → commonjs/src}/utils/session.d.ts +4 -0
  93. package/lib/typescript/module/src/NativeRumSdkReactnative.d.ts +49 -0
  94. package/lib/typescript/module/src/collector/action/index.d.ts +21 -0
  95. package/lib/typescript/module/src/collector/exception/crash.d.ts +28 -0
  96. package/lib/typescript/module/src/collector/exception/index.d.ts +32 -0
  97. package/lib/typescript/module/src/collector/index.d.ts +6 -0
  98. package/lib/typescript/module/src/collector/resource/api.d.ts +21 -0
  99. package/lib/typescript/module/src/collector/view/perf.d.ts +1 -0
  100. package/lib/typescript/module/src/collector/view/pv.d.ts +16 -0
  101. package/lib/typescript/module/src/expoConfigPlugin.d.ts +28 -0
  102. package/lib/typescript/module/src/index.d.ts +6 -0
  103. package/lib/typescript/module/src/plugin.d.ts +3 -0
  104. package/lib/typescript/module/src/processor/default-processor.d.ts +6 -0
  105. package/lib/typescript/module/src/processor/session-processor.d.ts +11 -0
  106. package/lib/typescript/module/src/reporter/index.d.ts +18 -0
  107. package/lib/typescript/module/src/reporter/monitor-reporter.d.ts +8 -0
  108. package/lib/typescript/module/src/shell.d.ts +57 -0
  109. package/lib/typescript/module/src/types/client.d.ts +27 -0
  110. package/lib/typescript/module/src/types/crash.d.ts +95 -0
  111. package/lib/typescript/module/src/types/index.d.ts +118 -0
  112. package/lib/typescript/module/src/utils/action.d.ts +63 -0
  113. package/lib/typescript/module/src/utils/api.d.ts +59 -0
  114. package/lib/typescript/module/src/utils/base.d.ts +8 -0
  115. package/lib/typescript/module/src/utils/crash.d.ts +12 -0
  116. package/lib/typescript/module/src/utils/platform.d.ts +22 -0
  117. package/lib/typescript/module/src/utils/session.d.ts +41 -0
  118. package/lib/typescript/module/src/utils/url.d.ts +17 -0
  119. package/lib/typescript/module/src/utils/view.d.ts +13 -0
  120. package/package.json +51 -28
  121. package/lib/module/collector/action/index.js.map +0 -1
  122. package/lib/module/collector/exception/index.js.map +0 -1
  123. package/lib/module/collector/resource/api.js.map +0 -1
  124. package/lib/module/collector/view/perf.js.map +0 -1
  125. package/lib/module/collector/view/pv.js.map +0 -1
  126. package/lib/module/index.js.map +0 -1
  127. package/lib/module/plugin.js.map +0 -1
  128. package/lib/module/processor/default-processor.js.map +0 -1
  129. package/lib/module/processor/session-processor.js.map +0 -1
  130. package/lib/module/reporter/index.js.map +0 -1
  131. package/lib/module/shell.js.map +0 -1
  132. package/lib/module/types/client.js.map +0 -1
  133. package/lib/module/types/index.js.map +0 -1
  134. package/lib/module/utils/api.js.map +0 -1
  135. package/lib/module/utils/base.js.map +0 -1
  136. package/lib/module/utils/platform.js.map +0 -1
  137. package/lib/module/utils/session.js.map +0 -1
  138. package/lib/module/utils/url.js.map +0 -1
  139. package/lib/module/utils/view.js.map +0 -1
  140. package/lib/typescript/src/collector/action/index.d.ts.map +0 -1
  141. package/lib/typescript/src/collector/exception/index.d.ts.map +0 -1
  142. package/lib/typescript/src/collector/resource/api.d.ts.map +0 -1
  143. package/lib/typescript/src/collector/view/perf.d.ts.map +0 -1
  144. package/lib/typescript/src/collector/view/pv.d.ts.map +0 -1
  145. package/lib/typescript/src/index.d.ts +0 -4
  146. package/lib/typescript/src/index.d.ts.map +0 -1
  147. package/lib/typescript/src/plugin.d.ts.map +0 -1
  148. package/lib/typescript/src/processor/default-processor.d.ts.map +0 -1
  149. package/lib/typescript/src/processor/session-processor.d.ts.map +0 -1
  150. package/lib/typescript/src/reporter/index.d.ts.map +0 -1
  151. package/lib/typescript/src/shell.d.ts.map +0 -1
  152. package/lib/typescript/src/types/client.d.ts.map +0 -1
  153. package/lib/typescript/src/types/index.d.ts.map +0 -1
  154. package/lib/typescript/src/utils/api.d.ts.map +0 -1
  155. package/lib/typescript/src/utils/base.d.ts.map +0 -1
  156. package/lib/typescript/src/utils/platform.d.ts +0 -15
  157. package/lib/typescript/src/utils/platform.d.ts.map +0 -1
  158. package/lib/typescript/src/utils/session.d.ts.map +0 -1
  159. package/lib/typescript/src/utils/url.d.ts.map +0 -1
  160. package/lib/typescript/src/utils/view.d.ts.map +0 -1
  161. /package/lib/typescript/{src → commonjs/src}/collector/resource/api.d.ts +0 -0
  162. /package/lib/typescript/{src → commonjs/src}/collector/view/perf.d.ts +0 -0
  163. /package/lib/typescript/{src → commonjs/src}/collector/view/pv.d.ts +0 -0
  164. /package/lib/typescript/{src → commonjs/src}/plugin.d.ts +0 -0
  165. /package/lib/typescript/{src → commonjs/src}/processor/default-processor.d.ts +0 -0
  166. /package/lib/typescript/{src → commonjs/src}/types/client.d.ts +0 -0
  167. /package/lib/typescript/{src → commonjs/src}/types/index.d.ts +0 -0
  168. /package/lib/typescript/{src → commonjs/src}/utils/api.d.ts +0 -0
  169. /package/lib/typescript/{src → commonjs/src}/utils/base.d.ts +0 -0
  170. /package/lib/typescript/{src → commonjs/src}/utils/url.d.ts +0 -0
  171. /package/lib/typescript/{src → commonjs/src}/utils/view.d.ts +0 -0
  172. /package/lib/typescript/{package.json → module/package.json} +0 -0
@@ -0,0 +1,144 @@
1
+ #include <unwind.h>
2
+ #include <stdint.h>
3
+ #include <sys/ucontext.h>
4
+
5
+ typedef struct {
6
+ void **frames;
7
+ int max_frames;
8
+ int count;
9
+ int skip; // skip top frames (e.g. signal handler itself)
10
+ } backtrace_state_t;
11
+
12
+ static _Unwind_Reason_Code unwind_callback(struct _Unwind_Context *context, void *arg) {
13
+ backtrace_state_t *state = (backtrace_state_t *)arg;
14
+
15
+ uintptr_t pc = _Unwind_GetIP(context);
16
+ if (pc == 0) {
17
+ return _URC_END_OF_STACK;
18
+ }
19
+
20
+ if (state->skip > 0) {
21
+ state->skip--;
22
+ } else if (state->count < state->max_frames) {
23
+ state->frames[state->count++] = (void *)pc;
24
+ }
25
+
26
+ if (state->count >= state->max_frames) {
27
+ return _URC_END_OF_STACK;
28
+ }
29
+ return _URC_NO_REASON;
30
+ }
31
+
32
+ #if defined(__arm__)
33
+ /*
34
+ * ARM32 frame pointer 遍历(async-signal-safe fallback)。
35
+ * _Unwind_Backtrace 在 ARM32 + 旧版 Android 上可能死锁,
36
+ * 使用 frame pointer 遍历作为更安全的替代方案。
37
+ * 要求编译选项 -fno-omit-frame-pointer(已在 CMakeLists 中启用)。
38
+ */
39
+ static int rum_fp_backtrace(void **buffer, int max_frames, ucontext_t *uc) {
40
+ if (buffer == NULL || max_frames <= 0 || uc == NULL) {
41
+ return 0;
42
+ }
43
+
44
+ int count = 0;
45
+ // 从崩溃 PC 开始
46
+ buffer[count++] = (void *)(uintptr_t)uc->uc_mcontext.arm_pc;
47
+
48
+ // 使用 fp (frame pointer) 遍历调用栈
49
+ void *fp = (void *)(uintptr_t)uc->uc_mcontext.arm_fp;
50
+ while (count < max_frames && fp != NULL) {
51
+ // ARM32 栈帧布局:[fp_prev, lr, ...]
52
+ void **frame = (void **)fp;
53
+ void *lr = frame[1]; // link register (return address)
54
+ void *next_fp = frame[0]; // previous frame pointer
55
+
56
+ if (lr == NULL) break;
57
+ buffer[count++] = lr;
58
+
59
+ // 防止循环(fp 未变化)
60
+ if (next_fp == fp || next_fp == NULL) break;
61
+ fp = next_fp;
62
+ }
63
+ return count;
64
+ }
65
+ #endif
66
+
67
+ #if defined(__aarch64__)
68
+ /*
69
+ * ARM64 frame pointer 遍历(async-signal-safe)。
70
+ * _Unwind_Backtrace 在信号上下文中可能持有加载器/解锁器锁,
71
+ * 存在死锁风险;arm64 有稳定的 x29 帧指针链,改用 fp 遍历更安全。
72
+ * 要求 -fno-omit-frame-pointer(已在 CMakeLists 中启用)。
73
+ *
74
+ * arm64 栈帧布局(AAPCS64):fp -> [saved_fp(x29), saved_lr(x30)]
75
+ * 权衡:穿越未开启帧指针的系统库(libc/libart 等)时可能提前截断,
76
+ * 栈略短于 .eh_frame unwinding,但避免了信号上下文加锁风险。
77
+ */
78
+ static int rum_fp_backtrace_arm64(void **buffer, int max_frames, ucontext_t *uc) {
79
+ if (buffer == NULL || max_frames <= 0 || uc == NULL) {
80
+ return 0;
81
+ }
82
+
83
+ int count = 0;
84
+ // frame[0] = 崩溃 PC
85
+ buffer[count++] = (void *)(uintptr_t)uc->uc_mcontext.pc;
86
+
87
+ void **fp = (void **)(uintptr_t)uc->uc_mcontext.regs[29];
88
+ while (count < max_frames && fp != NULL) {
89
+ void *next_fp = fp[0]; // 上一帧 fp
90
+ void *lr = fp[1]; // 返回地址
91
+
92
+ if (lr == NULL) break;
93
+ buffer[count++] = lr;
94
+
95
+ if (next_fp == NULL) break;
96
+ // fp 必须单调递增(栈向低地址增长,调用者 fp 更高),防止循环
97
+ if ((uintptr_t)next_fp <= (uintptr_t)fp) break;
98
+ // fp 必须 16 字节对齐(AAPCS64 栈对齐要求)
99
+ if ((uintptr_t)next_fp & 0xf) break;
100
+ fp = (void **)next_fp;
101
+ }
102
+ return count;
103
+ }
104
+ #endif
105
+
106
+ /*
107
+ * Capture backtrace.
108
+ * - arm64/x86: 使用 _Unwind_Backtrace(通常安全)
109
+ * - arm32: 使用 frame pointer 遍历(避免 _Unwind_Backtrace 死锁风险)
110
+ * If uc is provided, the crash PC is inserted as frame[0].
111
+ */
112
+ int rum_backtrace(void **buffer, int max_frames, ucontext_t *uc) {
113
+ if (buffer == NULL || max_frames <= 0) {
114
+ return 0;
115
+ }
116
+
117
+ #if defined(__arm__)
118
+ // ARM32: 使用 frame pointer 遍历,避免 _Unwind_Backtrace 死锁
119
+ return rum_fp_backtrace(buffer, max_frames, uc);
120
+ #elif defined(__aarch64__)
121
+ // ARM64: 使用 frame pointer 遍历,避免 _Unwind_Backtrace 信号内持锁死锁
122
+ return rum_fp_backtrace_arm64(buffer, max_frames, uc);
123
+ #else
124
+ // x86 / x86_64(主要为模拟器): 使用 _Unwind_Backtrace
125
+ backtrace_state_t state = { buffer, max_frames, 0, 0 };
126
+
127
+ // Insert crash PC first (per-architecture register extraction)
128
+ if (uc != NULL) {
129
+ #if defined(__aarch64__)
130
+ buffer[0] = (void *)(uintptr_t)uc->uc_mcontext.pc;
131
+ state.count = 1;
132
+ #elif defined(__x86_64__)
133
+ buffer[0] = (void *)(uintptr_t)uc->uc_mcontext.gregs[REG_RIP];
134
+ state.count = 1;
135
+ #elif defined(__i386__)
136
+ buffer[0] = (void *)(uintptr_t)uc->uc_mcontext.gregs[REG_EIP];
137
+ state.count = 1;
138
+ #endif
139
+ }
140
+
141
+ _Unwind_Backtrace(unwind_callback, &state);
142
+ return state.count;
143
+ #endif
144
+ }
@@ -0,0 +1,200 @@
1
+ #include <signal.h>
2
+ #include <string.h>
3
+ #include <unistd.h>
4
+ #include <fcntl.h>
5
+ #include <time.h>
6
+ #include <stdint.h>
7
+ #include <sys/stat.h>
8
+ #include <sys/ucontext.h>
9
+ #include <android/log.h>
10
+ #include <jni.h>
11
+
12
+ #define TAG "RumCrash"
13
+ // 帧采集上限:与 rum_crash_writer.c 的 TRUNCATION_THRESHOLD 对齐。
14
+ // 递归崩溃(如 StackOverflow)可能产生数千帧,512 帧足以覆盖
15
+ // 大多数崩溃场景,同时在信号处理器栈上占用可控(512 * sizeof(void*) = 4KB)。
16
+ #define MAX_FRAMES 512
17
+
18
+ // Signals to capture
19
+ static const int HANDLED_SIGNALS[] = { SIGSEGV, SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGTRAP };
20
+ #define NUM_SIGNALS 6
21
+
22
+ // Previous signal handlers (for chain calling)
23
+ static struct sigaction g_old_actions[NUM_SIGNALS];
24
+
25
+ // Crash output directory
26
+ static char g_crash_dir[512] = {0};
27
+ static size_t g_crash_dir_len = 0;
28
+
29
+ // Alternate signal stack (32KB+)
30
+ static char g_alt_stack[SIGSTKSZ * 4];
31
+
32
+ // Reentrancy guard
33
+ static volatile sig_atomic_t g_handling_crash = 0;
34
+
35
+ // Forward declarations (implemented in companion files)
36
+ extern int rum_backtrace(void **buffer, int max_frames, ucontext_t *uc);
37
+ extern void rum_write_crash(int fd, int signum, siginfo_t *info, ucontext_t *uc,
38
+ void **frames, int frame_count);
39
+
40
+ // async-signal-safe: convert non-negative long long to decimal string
41
+ // returns length written (no null terminator)
42
+ static int safe_ltoa(char *buf, long long val) {
43
+ if (val < 0) {
44
+ buf[0] = '-';
45
+ // 使用 unsigned long long 避免 LLONG_MIN 取反溢出
46
+ unsigned long long uval = (unsigned long long)(-(val + 1)) + 1ULL;
47
+ char tmp[32];
48
+ int n = 0;
49
+ while (uval > 0) {
50
+ tmp[n++] = (char)('0' + (uval % 10));
51
+ uval /= 10;
52
+ }
53
+ if (n == 0) tmp[n++] = '0';
54
+ for (int i = 0; i < n; i++) {
55
+ buf[1 + i] = tmp[n - 1 - i];
56
+ }
57
+ return 1 + n;
58
+ }
59
+ char tmp[32];
60
+ int n = 0;
61
+ if (val == 0) {
62
+ tmp[n++] = '0';
63
+ } else {
64
+ while (val > 0) {
65
+ tmp[n++] = (char)('0' + (val % 10));
66
+ val /= 10;
67
+ }
68
+ }
69
+ for (int i = 0; i < n; i++) {
70
+ buf[i] = tmp[n - 1 - i];
71
+ }
72
+ return n;
73
+ }
74
+
75
+ // async-signal-safe: append source string to buf at *pos, capped by cap
76
+ static void safe_append(char *buf, size_t *pos, size_t cap, const char *src) {
77
+ while (*src && *pos < cap - 1) {
78
+ buf[(*pos)++] = *src++;
79
+ }
80
+ }
81
+
82
+ static void crash_signal_handler(int signum, siginfo_t *info, void *context) {
83
+ // 1. Reentrancy guard
84
+ if (g_handling_crash) {
85
+ // Restore default and re-raise to allow process termination
86
+ signal(signum, SIG_DFL);
87
+ return;
88
+ }
89
+ g_handling_crash = 1;
90
+
91
+ ucontext_t *uc = (ucontext_t *)context;
92
+
93
+ // 2. Capture backtrace
94
+ void *frames[MAX_FRAMES];
95
+ int frame_count = rum_backtrace(frames, MAX_FRAMES, uc);
96
+
97
+ // 3. Build file path: {dir}/crash_native_{timestamp}_{pid}.json
98
+ // Manual concatenation (no sprintf, async-signal-safe).
99
+ char filepath[768];
100
+ size_t fp_pos = 0;
101
+ if (g_crash_dir_len > 0 && g_crash_dir_len < sizeof(filepath) - 100) {
102
+ memcpy(filepath, g_crash_dir, g_crash_dir_len);
103
+ fp_pos = g_crash_dir_len;
104
+ // ensure trailing slash
105
+ if (filepath[fp_pos - 1] != '/') {
106
+ filepath[fp_pos++] = '/';
107
+ }
108
+ }
109
+ safe_append(filepath, &fp_pos, sizeof(filepath), "crash_native_");
110
+
111
+ // timestamp via time(NULL) — async-signal-safe per POSIX
112
+ time_t now = time(NULL);
113
+ fp_pos += safe_ltoa(filepath + fp_pos, (long long)now);
114
+
115
+ // 追加 pid 防止同秒崩溃文件名冲突,同时作为 crash_id 的一部分
116
+ safe_append(filepath, &fp_pos, sizeof(filepath), "_");
117
+ fp_pos += safe_ltoa(filepath + fp_pos, (long long)getpid());
118
+
119
+ safe_append(filepath, &fp_pos, sizeof(filepath), ".json");
120
+ filepath[fp_pos] = '\0';
121
+
122
+ // 4. Open and write
123
+ int fd = open(filepath, O_WRONLY | O_CREAT | O_TRUNC, 0644);
124
+ if (fd >= 0) {
125
+ rum_write_crash(fd, signum, info, uc, frames, frame_count);
126
+ fsync(fd);
127
+ close(fd);
128
+ }
129
+
130
+ // 5. Chain to previous handler
131
+ for (int i = 0; i < NUM_SIGNALS; i++) {
132
+ if (HANDLED_SIGNALS[i] == signum) {
133
+ sigaction(signum, &g_old_actions[i], NULL);
134
+ if (g_old_actions[i].sa_flags & SA_SIGINFO) {
135
+ if (g_old_actions[i].sa_sigaction != NULL) {
136
+ g_old_actions[i].sa_sigaction(signum, info, context);
137
+ }
138
+ } else if (g_old_actions[i].sa_handler != SIG_DFL &&
139
+ g_old_actions[i].sa_handler != SIG_IGN &&
140
+ g_old_actions[i].sa_handler != NULL) {
141
+ g_old_actions[i].sa_handler(signum);
142
+ }
143
+ break;
144
+ }
145
+ }
146
+
147
+ // 6. Terminate process
148
+ _exit(128 + signum);
149
+ }
150
+
151
+ JNIEXPORT void JNICALL
152
+ Java_com_rumsdkreactnative_crash_NdkCrashHandler_nativeInstall(JNIEnv *env, jclass clazz, jstring crash_dir_path) {
153
+ (void)clazz;
154
+
155
+ if (crash_dir_path == NULL) {
156
+ __android_log_print(ANDROID_LOG_WARN, TAG, "nativeInstall: null crash_dir_path");
157
+ return;
158
+ }
159
+
160
+ const char *dir = (*env)->GetStringUTFChars(env, crash_dir_path, NULL);
161
+ if (dir != NULL) {
162
+ size_t len = strlen(dir);
163
+ if (len >= sizeof(g_crash_dir)) {
164
+ len = sizeof(g_crash_dir) - 1;
165
+ }
166
+ memcpy(g_crash_dir, dir, len);
167
+ g_crash_dir[len] = '\0';
168
+ g_crash_dir_len = len;
169
+ (*env)->ReleaseStringUTFChars(env, crash_dir_path, dir);
170
+ }
171
+
172
+ // Setup alternate signal stack (avoid stack overflow during crash)
173
+ stack_t ss;
174
+ memset(&ss, 0, sizeof(ss));
175
+ ss.ss_sp = g_alt_stack;
176
+ ss.ss_size = sizeof(g_alt_stack);
177
+ ss.ss_flags = 0;
178
+ sigaltstack(&ss, NULL);
179
+
180
+ // Setup sigaction template
181
+ struct sigaction sa;
182
+ memset(&sa, 0, sizeof(sa));
183
+ sa.sa_sigaction = crash_signal_handler;
184
+ sa.sa_flags = SA_SIGINFO | SA_ONSTACK;
185
+ sigemptyset(&sa.sa_mask);
186
+ for (int i = 0; i < NUM_SIGNALS; i++) {
187
+ sigaddset(&sa.sa_mask, HANDLED_SIGNALS[i]);
188
+ }
189
+
190
+ // Register for each signal, preserving previous handler for chain calling
191
+ for (int i = 0; i < NUM_SIGNALS; i++) {
192
+ if (sigaction(HANDLED_SIGNALS[i], &sa, &g_old_actions[i]) != 0) {
193
+ __android_log_print(ANDROID_LOG_WARN, TAG,
194
+ "Failed to install handler for signal %d", HANDLED_SIGNALS[i]);
195
+ }
196
+ }
197
+
198
+ __android_log_print(ANDROID_LOG_INFO, TAG,
199
+ "NDK crash handler installed at: %s", g_crash_dir);
200
+ }