@logbrew/react-native 0.1.22 → 0.1.24

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 (37) hide show
  1. package/LogBrewReactNative.podspec +1 -0
  2. package/README.md +70 -7
  3. package/android/CMakeLists.txt +7 -0
  4. package/android/build.gradle +12 -0
  5. package/android/src/main/cpp/android_diagnostics.cpp +363 -0
  6. package/android/src/main/java/co/logbrew/reactnative/AndroidDiagnosticsRuntime.java +214 -0
  7. package/android/src/main/java/co/logbrew/reactnative/AndroidNativeDiagnostics.java +363 -0
  8. package/android/src/main/java/co/logbrew/reactnative/AndroidNativeSignalStore.java +233 -0
  9. package/android/src/main/java/co/logbrew/reactnative/AndroidParentDirectorySync.java +12 -12
  10. package/android/src/main/java/co/logbrew/reactnative/EventRecordStore.java +15 -5
  11. package/android/src/main/java/co/logbrew/reactnative/FatalStoreModuleImpl.java +127 -156
  12. package/android/src/newarch/java/co/logbrew/reactnative/FatalStoreModule.java +17 -17
  13. package/android/src/oldarch/java/co/logbrew/reactnative/FatalStoreModule.java +17 -17
  14. package/android-native-diagnostics.d.ts +29 -0
  15. package/android-native-diagnostics.js +176 -0
  16. package/fatal-replay.cjs +35 -3
  17. package/global-errors.d.cts +3 -3
  18. package/global-errors.d.ts +3 -3
  19. package/global-errors.native.js +1 -12
  20. package/index.cjs +27 -5
  21. package/index.native.d.ts +4 -0
  22. package/index.native.js +29 -4
  23. package/ios/AppleDiagnostics/LBRNAppleNativeDiagnostics.swift +1 -1
  24. package/ios/GeneratedAppleDiagnostics/LogBrew/LogBrewLogger.swift +2 -10
  25. package/ios/GeneratedAppleDiagnostics/LogBrew/OperationTrace.swift +56 -0
  26. package/ios/GeneratedAppleDiagnostics/LogBrew/URLSessionTracer.swift +48 -56
  27. package/ios/GeneratedAppleDiagnostics/LogBrew/Validation.swift +6 -0
  28. package/ios/GeneratedAppleDiagnostics/LogBrewCrash/NativeCrashCorrelation.swift +0 -1
  29. package/ios/GeneratedAppleDiagnostics/SOURCE-MANIFEST.json +5 -4
  30. package/ios/LBRNFatalStoreModule.mm +34 -86
  31. package/package.json +17 -5
  32. package/persistent-delivery.native.js +4 -3
  33. package/resource-fetch.js +5 -463
  34. package/src/NativeLogBrewFatalStore.ts +6 -4
  35. package/android/src/main/java/co/logbrew/reactnative/FatalRecordStore.java +0 -623
  36. package/ios/LBRNFatalRecordStore.h +0 -25
  37. package/ios/LBRNFatalRecordStore.m +0 -542
@@ -0,0 +1,233 @@
1
+ package co.logbrew.reactnative;
2
+
3
+ import java.io.File;
4
+ import java.io.FileInputStream;
5
+ import java.io.FileOutputStream;
6
+ import java.nio.ByteBuffer;
7
+ import java.nio.ByteOrder;
8
+ import java.nio.charset.StandardCharsets;
9
+ import java.util.Arrays;
10
+ import java.util.regex.Pattern;
11
+ import java.util.zip.CRC32;
12
+
13
+ final class AndroidNativeSignalStore {
14
+ static final int MAGIC = 0x4c424e53;
15
+ static final int VERSION = 1;
16
+ static final int ID_BYTES = 96;
17
+ static final int UUID_BYTES = 36;
18
+ static final int ARCH_BYTES = 16;
19
+ static final int OFFSET_BYTES = 16;
20
+ static final int PROJECT_BYTES = 36 * 2;
21
+ static final int RELEASE_BYTES = 256 * 2;
22
+ static final int ENVIRONMENT_BYTES = 128 * 2;
23
+ static final int SERVICE_BYTES = 128 * 2;
24
+ static final int RECORD_BYTES =
25
+ 4 + 4 + 4 + 8 + ID_BYTES + UUID_BYTES + ARCH_BYTES + OFFSET_BYTES
26
+ + PROJECT_BYTES + RELEASE_BYTES + ENVIRONMENT_BYTES + SERVICE_BYTES + 4;
27
+
28
+ private static final Pattern ID = Pattern.compile("^evt_android_native_[a-z0-9_]{1,76}$");
29
+ private static final Pattern UUID =
30
+ Pattern.compile("^[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}$");
31
+ private static final Pattern OFFSET = Pattern.compile("^[0-9a-f]{16}$");
32
+ private static final Pattern ARCHITECTURE = Pattern.compile("^(?:arm|arm64|x86|x86_64)$");
33
+
34
+ private final File file;
35
+
36
+ AndroidNativeSignalStore(File file) {
37
+ this.file = canonicalFile(file);
38
+ }
39
+
40
+ synchronized Record read() {
41
+ if (!safeRegularFile(file) || file.length() != RECORD_BYTES) {
42
+ discardInvalid();
43
+ return null;
44
+ }
45
+ byte[] bytes = new byte[RECORD_BYTES];
46
+ try (FileInputStream stream = new FileInputStream(file)) {
47
+ if (stream.read(bytes) != RECORD_BYTES || stream.read() != -1) {
48
+ discardInvalid();
49
+ return null;
50
+ }
51
+ } catch (Exception error) {
52
+ return null;
53
+ }
54
+ ByteBuffer input = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN);
55
+ if (input.getInt() != MAGIC || input.getInt() != VERSION) {
56
+ discardInvalid();
57
+ return null;
58
+ }
59
+ int signal = input.getInt();
60
+ long timestampMs = input.getLong();
61
+ String id = ascii(input, ID_BYTES);
62
+ String imageUuid = ascii(input, UUID_BYTES);
63
+ String architecture = ascii(input, ARCH_BYTES);
64
+ String instructionOffset = ascii(input, OFFSET_BYTES);
65
+ String projectId = utf16(input, PROJECT_BYTES);
66
+ String release = utf16(input, RELEASE_BYTES);
67
+ String environment = utf16(input, ENVIRONMENT_BYTES);
68
+ String service = utf16(input, SERVICE_BYTES);
69
+ long expectedChecksum = Integer.toUnsignedLong(input.getInt());
70
+ CRC32 checksum = new CRC32();
71
+ checksum.update(bytes, 0, RECORD_BYTES - 4);
72
+ if (expectedChecksum != checksum.getValue()
73
+ || signal <= 0
74
+ || timestampMs <= 0
75
+ || !ID.matcher(id).matches()
76
+ || !UUID.matcher(imageUuid).matches()
77
+ || !ARCHITECTURE.matcher(architecture).matches()
78
+ || !OFFSET.matcher(instructionOffset).matches()
79
+ || !UUID.matcher(projectId).matches()
80
+ || !bounded(release, 256)
81
+ || !bounded(environment, 128)
82
+ || !bounded(service, 128)) {
83
+ discardInvalid();
84
+ return null;
85
+ }
86
+ return new Record(
87
+ id,
88
+ signal,
89
+ timestampMs,
90
+ imageUuid,
91
+ architecture,
92
+ instructionOffset,
93
+ projectId,
94
+ release,
95
+ environment,
96
+ service);
97
+ }
98
+
99
+ synchronized boolean prepare(EventRecordStore.ParentDirectorySync parentSync) {
100
+ if (file.exists()) {
101
+ if (!safeRegularFile(file)) {
102
+ return false;
103
+ }
104
+ read();
105
+ if (file.exists()) {
106
+ return false;
107
+ }
108
+ }
109
+ try (FileOutputStream output = new FileOutputStream(file, false)) {
110
+ output.write(new byte[RECORD_BYTES]);
111
+ output.getFD().sync();
112
+ return parentSync.sync(file.getParentFile())
113
+ != EventRecordStore.ParentDirectorySyncResult.FAILED;
114
+ } catch (Exception error) {
115
+ return false;
116
+ }
117
+ }
118
+
119
+ synchronized boolean clear() {
120
+ return !file.exists() || (safeRegularFile(file) && file.delete());
121
+ }
122
+
123
+ String path() {
124
+ return file.getAbsolutePath();
125
+ }
126
+
127
+ private void discardInvalid() {
128
+ if (safeRegularFile(file)) {
129
+ file.delete();
130
+ }
131
+ }
132
+
133
+ private static File canonicalFile(File candidate) {
134
+ try {
135
+ File parent = candidate.getParentFile();
136
+ return parent == null
137
+ ? candidate.getCanonicalFile()
138
+ : new File(parent.getCanonicalFile(), candidate.getName());
139
+ } catch (Exception error) {
140
+ return candidate.getAbsoluteFile();
141
+ }
142
+ }
143
+
144
+ private static boolean safeRegularFile(File candidate) {
145
+ try {
146
+ return candidate.exists()
147
+ && candidate.isFile()
148
+ && !java.nio.file.Files.isSymbolicLink(candidate.toPath())
149
+ && candidate.getCanonicalFile().equals(candidate.getAbsoluteFile());
150
+ } catch (Exception error) {
151
+ return false;
152
+ }
153
+ }
154
+
155
+ private static String ascii(ByteBuffer input, int width) {
156
+ byte[] bytes = new byte[width];
157
+ input.get(bytes);
158
+ int end = 0;
159
+ while (end < bytes.length && bytes[end] != 0) {
160
+ if (bytes[end] < 0x20 || bytes[end] > 0x7e) {
161
+ return "";
162
+ }
163
+ end += 1;
164
+ }
165
+ for (int index = end; index < bytes.length; index += 1) {
166
+ if (bytes[index] != 0) {
167
+ return "";
168
+ }
169
+ }
170
+ return new String(Arrays.copyOf(bytes, end), StandardCharsets.US_ASCII);
171
+ }
172
+
173
+ private static String utf16(ByteBuffer input, int bytes) {
174
+ char[] value = new char[bytes / 2];
175
+ int end = 0;
176
+ boolean terminated = false;
177
+ for (int index = 0; index < value.length; index += 1) {
178
+ char character = input.getChar();
179
+ if (character == 0) {
180
+ terminated = true;
181
+ } else if (terminated) {
182
+ return "";
183
+ } else {
184
+ value[end++] = character;
185
+ }
186
+ }
187
+ return new String(value, 0, end);
188
+ }
189
+
190
+ private static boolean bounded(String value, int maximum) {
191
+ return !value.isEmpty()
192
+ && value.length() <= maximum
193
+ && value.equals(value.trim())
194
+ && value.chars().noneMatch(
195
+ character -> character <= 31 || character >= 127 && character <= 159);
196
+ }
197
+
198
+ static final class Record {
199
+ final String id;
200
+ final int signal;
201
+ final long timestampMs;
202
+ final String imageUuid;
203
+ final String architecture;
204
+ final String instructionOffset;
205
+ final String projectId;
206
+ final String release;
207
+ final String environment;
208
+ final String service;
209
+
210
+ Record(
211
+ String id,
212
+ int signal,
213
+ long timestampMs,
214
+ String imageUuid,
215
+ String architecture,
216
+ String instructionOffset,
217
+ String projectId,
218
+ String release,
219
+ String environment,
220
+ String service) {
221
+ this.id = id;
222
+ this.signal = signal;
223
+ this.timestampMs = timestampMs;
224
+ this.imageUuid = imageUuid;
225
+ this.architecture = architecture;
226
+ this.instructionOffset = instructionOffset;
227
+ this.projectId = projectId;
228
+ this.release = release;
229
+ this.environment = environment;
230
+ this.service = service;
231
+ }
232
+ }
233
+ }
@@ -6,9 +6,9 @@ import android.system.OsConstants;
6
6
  import java.io.File;
7
7
  import java.io.FileDescriptor;
8
8
 
9
- final class AndroidParentDirectorySync implements FatalRecordStore.ParentDirectorySync {
9
+ final class AndroidParentDirectorySync implements EventRecordStore.ParentDirectorySync {
10
10
  @Override
11
- public FatalRecordStore.ParentDirectorySyncResult sync(File directory) {
11
+ public EventRecordStore.ParentDirectorySyncResult sync(File directory) {
12
12
  FileDescriptor descriptor;
13
13
  try {
14
14
  descriptor =
@@ -17,39 +17,39 @@ final class AndroidParentDirectorySync implements FatalRecordStore.ParentDirecto
17
17
  OsConstants.O_RDONLY | OsConstants.O_CLOEXEC | OsConstants.O_NOFOLLOW,
18
18
  0);
19
19
  } catch (ErrnoException | RuntimeException error) {
20
- return FatalRecordStore.ParentDirectorySyncResult.FAILED;
20
+ return EventRecordStore.ParentDirectorySyncResult.FAILED;
21
21
  }
22
22
 
23
- FatalRecordStore.ParentDirectorySyncResult result;
23
+ EventRecordStore.ParentDirectorySyncResult result;
24
24
  try {
25
25
  if (!OsConstants.S_ISDIR(Os.fstat(descriptor).st_mode)) {
26
- result = FatalRecordStore.ParentDirectorySyncResult.FAILED;
26
+ result = EventRecordStore.ParentDirectorySyncResult.FAILED;
27
27
  } else {
28
28
  result = syncDescriptor(descriptor);
29
29
  }
30
30
  } catch (ErrnoException | RuntimeException error) {
31
- result = FatalRecordStore.ParentDirectorySyncResult.FAILED;
31
+ result = EventRecordStore.ParentDirectorySyncResult.FAILED;
32
32
  }
33
33
 
34
34
  try {
35
35
  Os.close(descriptor);
36
36
  } catch (ErrnoException | RuntimeException error) {
37
- return FatalRecordStore.ParentDirectorySyncResult.FAILED;
37
+ return EventRecordStore.ParentDirectorySyncResult.FAILED;
38
38
  }
39
39
  return result;
40
40
  }
41
41
 
42
- private static FatalRecordStore.ParentDirectorySyncResult syncDescriptor(
42
+ private static EventRecordStore.ParentDirectorySyncResult syncDescriptor(
43
43
  FileDescriptor descriptor) {
44
44
  try {
45
45
  Os.fsync(descriptor);
46
- return FatalRecordStore.ParentDirectorySyncResult.SYNCHRONIZED;
46
+ return EventRecordStore.ParentDirectorySyncResult.SYNCHRONIZED;
47
47
  } catch (ErrnoException error) {
48
48
  return unsupportedDirectorySync(error.errno)
49
- ? FatalRecordStore.ParentDirectorySyncResult.UNSUPPORTED
50
- : FatalRecordStore.ParentDirectorySyncResult.FAILED;
49
+ ? EventRecordStore.ParentDirectorySyncResult.UNSUPPORTED
50
+ : EventRecordStore.ParentDirectorySyncResult.FAILED;
51
51
  } catch (RuntimeException error) {
52
- return FatalRecordStore.ParentDirectorySyncResult.FAILED;
52
+ return EventRecordStore.ParentDirectorySyncResult.FAILED;
53
53
  }
54
54
  }
55
55
 
@@ -34,11 +34,11 @@ final class EventRecordStore {
34
34
  Pattern.compile("^event-([0-9]{20})\\.record$");
35
35
  private static final Pattern MARKER_PATTERN =
36
36
  Pattern.compile("^accepted-([0-9]{20})\\.marker$");
37
- private static final FatalRecordStore.ParentDirectorySync UNSUPPORTED_PARENT_DIRECTORY_SYNC =
38
- directory -> FatalRecordStore.ParentDirectorySyncResult.UNSUPPORTED;
37
+ private static final ParentDirectorySync UNSUPPORTED_PARENT_DIRECTORY_SYNC =
38
+ directory -> ParentDirectorySyncResult.UNSUPPORTED;
39
39
 
40
40
  private final File directory;
41
- private final FatalRecordStore.ParentDirectorySync parentDirectorySync;
41
+ private final ParentDirectorySync parentDirectorySync;
42
42
  private boolean poisoned;
43
43
 
44
44
  EventRecordStore(File directory) {
@@ -46,7 +46,7 @@ final class EventRecordStore {
46
46
  }
47
47
 
48
48
  EventRecordStore(
49
- File directory, FatalRecordStore.ParentDirectorySync parentDirectorySync) {
49
+ File directory, ParentDirectorySync parentDirectorySync) {
50
50
  this.directory = resolveCanonicalParent(directory);
51
51
  this.parentDirectorySync = parentDirectorySync;
52
52
  }
@@ -332,7 +332,7 @@ final class EventRecordStore {
332
332
  return false;
333
333
  }
334
334
  if (parentDirectorySync.sync(directory)
335
- == FatalRecordStore.ParentDirectorySyncResult.FAILED) {
335
+ == ParentDirectorySyncResult.FAILED) {
336
336
  poisoned = true;
337
337
  return false;
338
338
  }
@@ -555,6 +555,16 @@ final class EventRecordStore {
555
555
  && value.setWritable(true, true);
556
556
  }
557
557
 
558
+ interface ParentDirectorySync {
559
+ ParentDirectorySyncResult sync(File directory);
560
+ }
561
+
562
+ enum ParentDirectorySyncResult {
563
+ SYNCHRONIZED,
564
+ UNSUPPORTED,
565
+ FAILED
566
+ }
567
+
558
568
  static final class Record {
559
569
  final long sequence;
560
570
  final String serializedEvent;