relay_event_normalization/
mechanism.rs

1use relay_event_schema::processor::{self, ProcessingAction};
2use relay_event_schema::protocol::{Event, Mechanism, OsContext};
3use relay_protocol::{Annotated, Error};
4
5fn get_errno_name(errno: i64, os_hint: OsHint) -> Option<&'static str> {
6    Some(match os_hint {
7        OsHint::Linux => match errno {
8            1 => "EPERM",    // Operation not permitted
9            2 => "ENOENT",   // No such file or directory
10            3 => "ESRCH",    // No such process
11            4 => "EINTR",    // Interrupted system call
12            5 => "EIO",      // I/O error
13            6 => "ENXIO",    // No such device or address
14            7 => "E2BIG",    // Argument list too long
15            8 => "ENOEXEC",  // Exec format error
16            9 => "EBADF",    // Bad file number
17            10 => "ECHILD",  // No child processes
18            11 => "EAGAIN",  // Try again
19            12 => "ENOMEM",  // Out of memory
20            13 => "EACCES",  // Permission denied
21            14 => "EFAULT",  // Bad address
22            15 => "ENOTBLK", // Block device required
23            16 => "EBUSY",   // Device or resource busy
24            17 => "EEXIST",  // File exists
25            18 => "EXDEV",   // Cross-device link
26            19 => "ENODEV",  // No such device
27            20 => "ENOTDIR", // Not a directory
28            21 => "EISDIR",  // Is a directory
29            22 => "EINVAL",  // Invalid argument
30            23 => "ENFILE",  // File table overflow
31            24 => "EMFILE",  // Too many open files
32            25 => "ENOTTY",  // Not a typewriter
33            26 => "ETXTBSY", // Text file busy
34            27 => "EFBIG",   // File too large
35            28 => "ENOSPC",  // No space left on device
36            29 => "ESPIPE",  // Illegal seek
37            30 => "EROFS",   // Read-only file system
38            31 => "EMLINK",  // Too many links
39            32 => "EPIPE",   // Broken pipe
40            33 => "EDOM",    // Math argument out of domain of func
41            34 => "ERANGE",  // Math result not representable
42
43            35 => "EDEADLK",      // Resource deadlock would occur
44            36 => "ENAMETOOLONG", // File name too long
45            37 => "ENOLCK",       // No record locks available
46
47            38 => "ENOSYS", // Invalid system call number
48
49            39 => "ENOTEMPTY", // Directory not empty
50            40 => "ELOOP",     // Too many symbolic links encountered
51            42 => "ENOMSG",    // No message of desired type
52            43 => "EIDRM",     // Identifier removed
53            44 => "ECHRNG",    // Channel number out of range
54            45 => "EL2NSYNC",  // Level 2 not synchronized
55            46 => "EL3HLT",    // Level 3 halted
56            47 => "EL3RST",    // Level 3 reset
57            48 => "ELNRNG",    // Link number out of range
58            49 => "EUNATCH",   // Protocol driver not attached
59            50 => "ENOCSI",    // No CSI structure available
60            51 => "EL2HLT",    // Level 2 halted
61            52 => "EBADE",     // Invalid exchange
62            53 => "EBADR",     // Invalid request descriptor
63            54 => "EXFULL",    // Exchange full
64            55 => "ENOANO",    // No anode
65            56 => "EBADRQC",   // Invalid request code
66            57 => "EBADSLT",   // Invalid slot
67
68            59 => "EBFONT",          // Bad font file format
69            60 => "ENOSTR",          // Device not a stream
70            61 => "ENODATA",         // No data available
71            62 => "ETIME",           // Timer expired
72            63 => "ENOSR",           // Out of streams resources
73            64 => "ENONET",          // Machine is not on the network
74            65 => "ENOPKG",          // Package not installed
75            66 => "EREMOTE",         // Object is remote
76            67 => "ENOLINK",         // Link has been severed
77            68 => "EADV",            // Advertise error
78            69 => "ESRMNT",          // Srmount error
79            70 => "ECOMM",           // Communication error on send
80            71 => "EPROTO",          // Protocol error
81            72 => "EMULTIHOP",       // Multihop attempted
82            73 => "EDOTDOT",         // RFS specific error
83            74 => "EBADMSG",         // Not a data message
84            75 => "EOVERFLOW",       // Value too large for defined data type
85            76 => "ENOTUNIQ",        // Name not unique on network
86            77 => "EBADFD",          // File descriptor in bad state
87            78 => "EREMCHG",         // Remote address changed
88            79 => "ELIBACC",         // Can not access a needed shared library
89            80 => "ELIBBAD",         // Accessing a corrupted shared library
90            81 => "ELIBSCN",         // .lib section in a.out corrupted
91            82 => "ELIBMAX",         // Attempting to link in too many shared libraries
92            83 => "ELIBEXEC",        // Cannot exec a shared library directly
93            84 => "EILSEQ",          // Illegal byte sequence
94            85 => "ERESTART",        // Interrupted system call should be restarted
95            86 => "ESTRPIPE",        // Streams pipe error
96            87 => "EUSERS",          // Too many users
97            88 => "ENOTSOCK",        // Socket operation on non-socket
98            89 => "EDESTADDRREQ",    // Destination address required
99            90 => "EMSGSIZE",        // Message too long
100            91 => "EPROTOTYPE",      // Protocol wrong type for socket
101            92 => "ENOPROTOOPT",     // Protocol not available
102            93 => "EPROTONOSUPPORT", // Protocol not supported
103            94 => "ESOCKTNOSUPPORT", // Socket type not supported
104            95 => "EOPNOTSUPP",      // Operation not supported on transport endpoint
105            96 => "EPFNOSUPPORT",    // Protocol family not supported
106            97 => "EAFNOSUPPORT",    // Address family not supported by protocol
107            98 => "EADDRINUSE",      // Address already in use
108            99 => "EADDRNOTAVAIL",   // Cannot assign requested address
109            100 => "ENETDOWN",       // Network is down
110            101 => "ENETUNREACH",    // Network is unreachable
111            102 => "ENETRESET",      // Network dropped connection because of reset
112            103 => "ECONNABORTED",   // Software caused connection abort
113            104 => "ECONNRESET",     // Connection reset by peer
114            105 => "ENOBUFS",        // No buffer space available
115            106 => "EISCONN",        // Transport endpoint is already connected
116            107 => "ENOTCONN",       // Transport endpoint is not connected
117            108 => "ESHUTDOWN",      // Cannot send after transport endpoint shutdown
118            109 => "ETOOMANYREFS",   // Too many references: cannot splice
119            110 => "ETIMEDOUT",      // Connection timed out
120            111 => "ECONNREFUSED",   // Connection refused
121            112 => "EHOSTDOWN",      // Host is down
122            113 => "EHOSTUNREACH",   // No route to host
123            114 => "EALREADY",       // Operation already in progress
124            115 => "EINPROGRESS",    // Operation now in progress
125            116 => "ESTALE",         // Stale file handle
126            117 => "EUCLEAN",        // Structure needs cleaning
127            118 => "ENOTNAM",        // Not a XENIX named type file
128            119 => "ENAVAIL",        // No XENIX semaphores available
129            120 => "EISNAM",         // Is a named type file
130            121 => "EREMOTEIO",      // Remote I/O error
131            122 => "EDQUOT",         // Quota exceeded
132
133            123 => "ENOMEDIUM",    // No medium found
134            124 => "EMEDIUMTYPE",  // Wrong medium type
135            125 => "ECANCELED",    // Operation Canceled
136            126 => "ENOKEY",       // Required key not available
137            127 => "EKEYEXPIRED",  // Key has expired
138            128 => "EKEYREVOKED",  // Key has been revoked
139            129 => "EKEYREJECTED", // Key was rejected by service
140
141            130 => "EOWNERDEAD",      // Owner died
142            131 => "ENOTRECOVERABLE", // State not recoverable
143
144            132 => "ERFKILL", // Operation not possible due to RF-kill
145
146            133 => "EHWPOISON", // Memory page has hardware error
147            _ => return None,
148        },
149        OsHint::Darwin => match errno {
150            1 => "EPERM",    // Operation not permitted
151            2 => "ENOENT",   // No such file or directory
152            3 => "ESRCH",    // No such process
153            4 => "EINTR",    // Interrupted system call
154            5 => "EIO",      // Input/output error
155            6 => "ENXIO",    // Device not configured
156            7 => "E2BIG",    // Argument list too long
157            8 => "ENOEXEC",  // Exec format error
158            9 => "EBADF",    // Bad file descriptor
159            10 => "ECHILD",  // No child processes
160            11 => "EDEADLK", // Resource deadlock avoided
161            12 => "ENOMEM",  // Cannot allocate memory
162            13 => "EACCES",  // Permission denied
163            14 => "EFAULT",  // Bad address
164            15 => "ENOTBLK", // Block device required
165            16 => "EBUSY",   // Device / Resource busy
166            17 => "EEXIST",  // File exists
167            18 => "EXDEV",   // Cross-device link
168            19 => "ENODEV",  // Operation not supported by device
169            20 => "ENOTDIR", // Not a directory
170            21 => "EISDIR",  // Is a directory
171            22 => "EINVAL",  // Invalid argument
172            23 => "ENFILE",  // Too many open files in system
173            24 => "EMFILE",  // Too many open files
174            25 => "ENOTTY",  // Inappropriate ioctl for device
175            26 => "ETXTBSY", // Text file busy
176            27 => "EFBIG",   // File too large
177            28 => "ENOSPC",  // No space left on device
178            29 => "ESPIPE",  // Illegal seek
179            30 => "EROFS",   // Read-only file system
180            31 => "EMLINK",  // Too many links
181            32 => "EPIPE",   // Broken pipe
182
183            // math software
184            33 => "EDOM",   // Numerical argument out of domain
185            34 => "ERANGE", // Result too large
186
187            // non - blocking and interrupt i / o
188            35 => "EAGAIN",      // Resource temporarily unavailable
189            36 => "EINPROGRESS", // Operation now in progress
190            37 => "EALREADY",    // Operation already in progress
191
192            // ipc / network software - - argument errors
193            38 => "ENOTSOCK",        // Socket operation on non-socket
194            39 => "EDESTADDRREQ",    // Destination address required
195            40 => "EMSGSIZE",        // Message too long
196            41 => "EPROTOTYPE",      // Protocol wrong type for socket
197            42 => "ENOPROTOOPT",     // Protocol not available
198            43 => "EPROTONOSUPPORT", // Protocol not supported
199            44 => "ESOCKTNOSUPPORT", // Socket type not supported
200            45 => "ENOTSUP",         // Operation not supported
201
202            46 => "EPFNOSUPPORT",  // Protocol family not supported
203            47 => "EAFNOSUPPORT",  // Address family not supported by protocol family
204            48 => "EADDRINUSE",    // Address already in use
205            49 => "EADDRNOTAVAIL", // Can"t assign requested address
206
207            // ipc / network software - - operational errors
208            50 => "ENETDOWN",     // Network is down
209            51 => "ENETUNREACH",  // Network is unreachable
210            52 => "ENETRESET",    // Network dropped connection on reset
211            53 => "ECONNABORTED", // Software caused connection abort
212            54 => "ECONNRESET",   // Connection reset by peer
213            55 => "ENOBUFS",      // No buffer space available
214            56 => "EISCONN",      // Socket is already connected
215            57 => "ENOTCONN",     // Socket is not connected
216            58 => "ESHUTDOWN",    // Can"t send after socket shutdown
217            59 => "ETOOMANYREFS", // Too many references: can"t splice
218            60 => "ETIMEDOUT",    // Operation timed out
219            61 => "ECONNREFUSED", // Connection refused
220
221            62 => "ELOOP",        // Too many levels of symbolic links
222            63 => "ENAMETOOLONG", // File name too long
223
224            // should be rearranged
225            64 => "EHOSTDOWN",    // Host is down
226            65 => "EHOSTUNREACH", // No route to host
227            66 => "ENOTEMPTY",    // Directory not empty
228
229            // quotas & mush
230            67 => "EPROCLIM", // Too many processes
231            68 => "EUSERS",   // Too many users
232            69 => "EDQUOT",   // Disc quota exceeded
233
234            // Network File System
235            70 => "ESTALE",        // Stale NFS file handle
236            71 => "EREMOTE",       // Too many levels of remote in path
237            72 => "EBADRPC",       // RPC struct is bad
238            73 => "ERPCMISMATCH",  // RPC version wrong
239            74 => "EPROGUNAVAIL",  // RPC prog. not avail
240            75 => "EPROGMISMATCH", // Program version wrong
241            76 => "EPROCUNAVAIL",  // Bad procedure for program
242
243            77 => "ENOLCK", // No locks available
244            78 => "ENOSYS", // Function not implemented
245
246            79 => "EFTYPE",    // Inappropriate file type or format
247            80 => "EAUTH",     // Authentication error
248            81 => "ENEEDAUTH", // Need authenticator
249
250            // Intelligent device errors
251            82 => "EPWROFF", // Device power is off
252            83 => "EDEVERR", // Device error, e.g. paper out
253
254            84 => "EOVERFLOW", // Value too large to be stored in data type
255
256            // Program loading errors
257            85 => "EBADEXEC",   // Bad executable
258            86 => "EBADARCH",   // Bad CPU type in executable
259            87 => "ESHLIBVERS", // Shared library version mismatch
260            88 => "EBADMACHO",  // Malformed Macho file
261
262            89 => "ECANCELED", // Operation canceled
263
264            90 => "EIDRM",   // Identifier removed
265            91 => "ENOMSG",  // No message of desired type
266            92 => "EILSEQ",  // Illegal byte sequence
267            93 => "ENOATTR", // Attribute not found
268
269            94 => "EBADMSG",   // Bad message
270            95 => "EMULTIHOP", // Reserved
271            96 => "ENODATA",   // No message available on STREAM
272            97 => "ENOLINK",   // Reserved
273            98 => "ENOSR",     // No STREAM resources
274            99 => "ENOSTR",    // Not a STREAM
275            100 => "EPROTO",   // Protocol error
276            101 => "ETIME",    // STREAM ioctl timeout
277
278            102 => "EOPNOTSUPP",      // Operation not supported on socket
279            103 => "ENOPOLICY",       // No such policy registered
280            104 => "ENOTRECOVERABLE", // State not recoverable
281            105 => "EOWNERDEAD",      // Previous owner died
282            106 => "EQFULL",          // Interface output queue is full
283            _ => return None,
284        },
285        OsHint::Windows => match errno {
286            1 => "EPERM",
287            2 => "ENOENT",
288            3 => "ESRCH",
289            4 => "EINTR",
290            5 => "EIO",
291            6 => "ENXIO",
292            7 => "E2BIG",
293            8 => "ENOEXEC",
294            9 => "EBADF",
295            10 => "ECHILD",
296            11 => "EAGAIN",
297            12 => "ENOMEM",
298            13 => "EACCES",
299            14 => "EFAULT",
300            16 => "EBUSY",
301            17 => "EEXIST",
302            18 => "EXDEV",
303            19 => "ENODEV",
304            20 => "ENOTDIR",
305            21 => "EISDIR",
306            23 => "ENFILE",
307            24 => "EMFILE",
308            25 => "ENOTTY",
309            27 => "EFBIG",
310            28 => "ENOSPC",
311            29 => "ESPIPE",
312            30 => "EROFS",
313            31 => "EMLINK",
314            32 => "EPIPE",
315            33 => "EDOM",
316            36 => "EDEADLK",
317            38 => "ENAMETOOLONG",
318            39 => "ENOLCK",
319            40 => "ENOSYS",
320            41 => "ENOTEMPTY",
321
322            // Error codes used in the Secure CRT functions
323            22 => "EINVAL",
324            34 => "ERANGE",
325            42 => "EILSEQ",
326            80 => "STRUNCATE",
327
328            // POSIX Supplement
329            100 => "EADDRINUSE",
330            101 => "EADDRNOTAVAIL",
331            102 => "EAFNOSUPPORT",
332            103 => "EALREADY",
333            104 => "EBADMSG",
334            105 => "ECANCELED",
335            106 => "ECONNABORTED",
336            107 => "ECONNREFUSED",
337            108 => "ECONNRESET",
338            109 => "EDESTADDRREQ",
339            110 => "EHOSTUNREACH",
340            111 => "EIDRM",
341            112 => "EINPROGRESS",
342            113 => "EISCONN",
343            114 => "ELOOP",
344            115 => "EMSGSIZE",
345            116 => "ENETDOWN",
346            117 => "ENETRESET",
347            118 => "ENETUNREACH",
348            119 => "ENOBUFS",
349            120 => "ENODATA",
350            121 => "ENOLINK",
351            122 => "ENOMSG",
352            123 => "ENOPROTOOPT",
353            124 => "ENOSR",
354            125 => "ENOSTR",
355            126 => "ENOTCONN",
356            127 => "ENOTRECOVERABLE",
357            128 => "ENOTSOCK",
358            129 => "ENOTSUP",
359            130 => "EOPNOTSUPP",
360            131 => "EOTHER",
361            132 => "EOVERFLOW",
362            133 => "EOWNERDEAD",
363            134 => "EPROTO",
364            135 => "EPROTONOSUPPORT",
365            136 => "EPROTOTYPE",
366            137 => "ETIME",
367            138 => "ETIMEDOUT",
368            139 => "ETXTBSY",
369            140 => "EWOULDBLOCK",
370            _ => return None,
371        },
372    })
373}
374
375fn get_signal_name(signo: i64, os_hint: OsHint) -> Option<&'static str> {
376    // Linux signals have been taken from <uapi/asm-generic/signal.h>
377    Some(match os_hint {
378        OsHint::Linux => match signo {
379            1 => "SIGHUP",  // Hangup.
380            2 => "SIGINT",  // Terminal interrupt signal.
381            3 => "SIGQUIT", // Terminal quit signal.
382            4 => "SIGILL",  // Illegal instruction.
383            5 => "SIGTRAP",
384            6 => "SIGABRT", // Process abort signal.
385            7 => "SIGBUS",
386            8 => "SIGFPE",   // Erroneous arithmetic operation.
387            9 => "SIGKILL",  // Kill (cannot be caught or ignored).
388            10 => "SIGUSR1", // User-defined signal 1.
389            11 => "SIGSEGV", // Invalid memory reference.
390            12 => "SIGUSR2", // User-defined signal 2.
391            13 => "SIGPIPE", // Write on a pipe with no one to read it.
392            14 => "SIGALRM", // Alarm clock.
393            15 => "SIGTERM", // Termination signal.
394            16 => "SIGSTKFLT",
395            17 => "SIGCHLD",   // Child process terminated or stopped.
396            18 => "SIGCONT",   // Continue executing, if stopped.
397            19 => "SIGSTOP",   // Stop executing (cannot be caught or ignored).
398            20 => "SIGTSTP",   // Terminal stop signal.
399            21 => "SIGTTIN",   // Background process attempting read.
400            22 => "SIGTTOU",   // Background process attempting write.
401            23 => "SIGURG",    // High bandwidth data is available at a socket.
402            24 => "SIGXCPU",   // CPU time limit exceeded.
403            25 => "SIGXFSZ",   // File size limit exceeded.
404            26 => "SIGVTALRM", // Virtual timer expired.
405            27 => "SIGPROF",   // Profiling timer expired.
406            28 => "SIGWINCH",
407            29 => "SIGIO",
408            30 => "SIGPWR",
409            31 => "SIGSYS",
410            _ => return None,
411        },
412        OsHint::Darwin => match signo {
413            1 => "SIGHUP",  // hangup
414            2 => "SIGINT",  // interrupt
415            3 => "SIGQUIT", // quit
416            4 => "SIGILL",  // illegal instruction (not reset when caught)
417            5 => "SIGTRAP", // trace trap (not reset when caught)
418            6 => "SIGABRT", // abort()
419            // if (defined(_POSIX_C_SOURCE) && !defined(_DARWIN_C_SOURCE))
420            7 => "SIGPOLL", // pollable event ([XSR] generated, not supported)
421            // if (!_POSIX_C_SOURCE || _DARWIN_C_SOURCE)
422            // 7 => "SIGEMT", // EMT instruction
423            8 => "SIGFPE",     // floating point exception
424            9 => "SIGKILL",    // kill (cannot be caught or ignored)
425            10 => "SIGBUS",    // bus error
426            11 => "SIGSEGV",   // segmentation violation
427            12 => "SIGSYS",    // bad argument to system call
428            13 => "SIGPIPE",   // write on a pipe with no one to read it
429            14 => "SIGALRM",   // alarm clock
430            15 => "SIGTERM",   // software termination signal from kill
431            16 => "SIGURG",    // urgent condition on IO channel
432            17 => "SIGSTOP",   // sendable stop signal not from tty
433            18 => "SIGTSTP",   // stop signal from tty
434            19 => "SIGCONT",   // continue a stopped process
435            20 => "SIGCHLD",   // to parent on child stop or exit
436            21 => "SIGTTIN",   // to readers pgrp upon background tty read
437            22 => "SIGTTOU",   // like TTIN for output if (tp->t_local&LTOSTOP)
438            23 => "SIGIO",     // input/output possible signal
439            24 => "SIGXCPU",   // exceeded CPU time limit
440            25 => "SIGXFSZ",   // exceeded file size limit
441            26 => "SIGVTALRM", // virtual time alarm
442            27 => "SIGPROF",   // profiling time alarm
443            28 => "SIGWINCH",  // window size changes
444            29 => "SIGINFO",   // information request
445            30 => "SIGUSR1",   // user defined signal 1
446            31 => "SIGUSR2",   // user defined signal 2
447            _ => return None,
448        },
449        _ => return None,
450    })
451}
452
453fn get_signal_code_name(signo: i64, codeno: i64) -> Option<&'static str> {
454    // Codes for Darwin `si_code`
455    Some(match signo {
456        // Codes for SIGILL
457        4 => match codeno {
458            0 => "ILL_NOOP",   // if only I knew...
459            1 => "ILL_ILLOPC", // [XSI] illegal opcode
460            2 => "ILL_ILLTRP", // [XSI] illegal trap
461            3 => "ILL_PRVOPC", // [XSI] privileged opcode
462            4 => "ILL_ILLOPN", // [XSI] illegal operand -NOTIMP
463            5 => "ILL_ILLADR", // [XSI] illegal addressing mode -NOTIMP
464            6 => "ILL_PRVREG", // [XSI] privileged register -NOTIMP
465            7 => "ILL_COPROC", // [XSI] coprocessor error -NOTIMP
466            8 => "ILL_BADSTK", // [XSI] internal stack error -NOTIMP
467            _ => return None,
468        },
469
470        // Codes for SIGFPE
471        8 => match codeno {
472            0 => "FPE_NOOP",   // if only I knew...
473            1 => "FPE_FLTDIV", // [XSI] floating point divide by zero
474            2 => "FPE_FLTOVF", // [XSI] floating point overflow
475            3 => "FPE_FLTUND", // [XSI] floating point underflow
476            4 => "FPE_FLTRES", // [XSI] floating point inexact result
477            5 => "FPE_FLTINV", // [XSI] invalid floating point operation
478            6 => "FPE_FLTSUB", // [XSI] subscript out of range -NOTIMP
479            7 => "FPE_INTDIV", // [XSI] integer divide by zero
480            8 => "FPE_INTOVF", // [XSI] integer overflow
481            _ => return None,
482        },
483
484        // Codes for SIGSEGV
485        11 => match codeno {
486            0 => "SEGV_NOOP",   // if only I knew...
487            1 => "SEGV_MAPERR", // [XSI] address not mapped to object
488            2 => "SEGV_ACCERR", // [XSI] invalid permission for mapped object
489            _ => return None,
490        },
491
492        // Codes for SIGBUS
493        10 => match codeno {
494            0 => "BUS_NOOP",   // if only I knew...
495            1 => "BUS_ADRALN", // [XSI] Invalid address alignment
496            2 => "BUS_ADRERR", // [XSI] Nonexistent physical address -NOTIMP
497            3 => "BUS_OBJERR", // [XSI] Object-specific HW error - NOTIMP
498            _ => return None,
499        },
500
501        // Codes for SIGTRAP
502        5 => match codeno {
503            1 => "TRAP_BRKPT", // [XSI] Process breakpoint -NOTIMP
504            2 => "TRAP_TRACE", // [XSI] Process trace trap -NOTIMP
505            _ => return None,
506        },
507
508        // Codes for SIGCHLD
509        20 => match codeno {
510            0 => "CLD_NOOP",      // if only I knew...
511            1 => "CLD_EXITED",    // [XSI] child has exited
512            2 => "CLD_KILLED",    // [XSI] terminated abnormally, no core file
513            3 => "CLD_DUMPED",    // [XSI] terminated abnormally, core file
514            4 => "CLD_TRAPPED",   // [XSI] traced child has trapped
515            5 => "CLD_STOPPED",   // [XSI] child has stopped
516            6 => "CLD_CONTINUED", // [XSI] stopped child has continued
517            _ => return None,
518        },
519
520        // Codes for SIGPOLL
521        7 => match codeno {
522            1 => "POLL_IN",  // [XSR] Data input available
523            2 => "POLL_OUT", // [XSR] Output buffers available
524            3 => "POLL_MSG", // [XSR] Input message available
525            4 => "POLL_ERR", // [XSR] I/O error
526            5 => "POLL_PRI", // [XSR] High priority input available
527            6 => "POLL_HUP", // [XSR] Device disconnected
528            _ => return None,
529        },
530        _ => return None,
531    })
532}
533
534fn get_mach_exception_name(number: i64) -> Option<&'static str> {
535    // Mach exception codes used in Darwin.
536    Some(match number {
537        1 => "EXC_BAD_ACCESS",      // Could not access memory
538        2 => "EXC_BAD_INSTRUCTION", // Instruction failed
539        3 => "EXC_ARITHMETIC",      // Arithmetic exception
540        4 => "EXC_EMULATION",       // Emulation instruction
541        5 => "EXC_SOFTWARE",        // Software generated exception
542        6 => "EXC_BREAKPOINT",      // Trace, breakpoint, etc.
543        7 => "EXC_SYSCALL",         // System calls.
544        8 => "EXC_MACH_SYSCALL",    // Mach system calls.
545        9 => "EXC_RPC_ALERT",       // RPC alert
546        10 => "EXC_CRASH",          // Abnormal process exit
547        11 => "EXC_RESOURCE",       // Hit resource consumption limit
548        12 => "EXC_GUARD",          // Violated guarded resource protections
549        13 => "EXC_CORPSE_NOTIFY",  // Abnormal process exited to corpse state
550        _ => return None,
551    })
552}
553
554/// Internal utility trait to indicate the OS.
555#[derive(Debug, PartialEq, Clone, Copy)]
556pub enum OsHint {
557    Windows,
558    Linux,
559    Darwin,
560}
561
562impl OsHint {
563    fn from_name(name: &str) -> Option<OsHint> {
564        match name.to_lowercase().as_ref() {
565            "ios" | "watchos" | "tvos" | "macos" => Some(OsHint::Darwin),
566            "linux" | "android" => Some(OsHint::Linux),
567            "windows" => Some(OsHint::Windows),
568            _ => None,
569        }
570    }
571
572    pub fn from_event(event: &Event) -> Option<OsHint> {
573        if let Some(debug_meta) = event.debug_meta.value() {
574            if let Some(sdk_info) = debug_meta.system_sdk.value() {
575                if let Some(name) = sdk_info.sdk_name.as_str() {
576                    return Self::from_name(name);
577                }
578            }
579        }
580
581        if let Some(os_context) = event.context::<OsContext>() {
582            if let Some(name) = os_context.name.as_str() {
583                return Self::from_name(name);
584            }
585        }
586
587        None
588    }
589}
590
591/// Normalizes the exception mechanism in place.
592pub fn normalize_mechanism(mechanism: &mut Mechanism, os_hint: Option<OsHint>) {
593    let _ = processor::apply(&mut mechanism.help_link, |value, meta| {
594        if value.starts_with("http://") || value.starts_with("https://") {
595            Ok(())
596        } else {
597            meta.add_error(Error::expected("http URL"));
598            Err(ProcessingAction::DeleteValueSoft)
599        }
600    });
601
602    let meta = match mechanism.meta.value_mut() {
603        Some(meta) => meta,
604        None => return,
605    };
606
607    if let Some(os_hint) = os_hint {
608        if let Some(cerror) = meta.errno.value_mut() {
609            if cerror.name.value().is_none() {
610                if let Some(errno) = cerror.number.value() {
611                    if let Some(name) = get_errno_name(*errno, os_hint) {
612                        cerror.name = Annotated::new(name.to_owned());
613                    }
614                }
615            }
616        }
617
618        if let Some(signal) = meta.signal.value_mut() {
619            if let Some(signo) = signal.number.value() {
620                if signal.name.value().is_none() {
621                    if let Some(name) = get_signal_name(*signo, os_hint) {
622                        signal.name = Annotated::new(name.to_owned());
623                    }
624                }
625
626                if os_hint == OsHint::Darwin && signal.code_name.value().is_none() {
627                    if let Some(code) = signal.code.value() {
628                        if let Some(code_name) = get_signal_code_name(*signo, *code) {
629                            signal.code_name = Annotated::new(code_name.to_owned());
630                        }
631                    }
632                }
633            }
634        }
635    }
636
637    if let Some(mach_exception) = meta.mach_exception.value_mut() {
638        if let Some(number) = mach_exception.ty.value() {
639            if mach_exception.name.value().is_none() {
640                if let Some(name) = get_mach_exception_name(*number) {
641                    mach_exception.name = Annotated::new(name.to_owned());
642                }
643            }
644        }
645    }
646}
647
648#[cfg(test)]
649mod tests {
650    use relay_event_schema::protocol::{CError, MachException, MechanismMeta, PosixSignal};
651    use relay_protocol::SerializableAnnotated;
652    use similar_asserts::assert_eq;
653
654    use super::*;
655
656    #[test]
657    fn test_normalize_missing() {
658        let mut mechanism = Mechanism {
659            ty: Annotated::new("generic".to_owned()),
660            ..Default::default()
661        };
662
663        let old_mechanism = mechanism.clone();
664
665        normalize_mechanism(&mut mechanism, None);
666
667        assert_eq!(mechanism, old_mechanism);
668    }
669
670    #[test]
671    fn test_normalize_errno() {
672        let mut mechanism = Mechanism {
673            ty: Annotated::new("generic".to_owned()),
674            meta: Annotated::new(MechanismMeta {
675                errno: Annotated::new(CError {
676                    number: Annotated::new(2),
677                    ..Default::default()
678                }),
679                ..Default::default()
680            }),
681            ..Default::default()
682        };
683
684        normalize_mechanism(&mut mechanism, Some(OsHint::Linux));
685
686        let errno = mechanism.meta.value().unwrap().errno.value().unwrap();
687        assert_eq!(
688            errno,
689            &CError {
690                number: Annotated::new(2),
691                name: Annotated::new("ENOENT".to_owned()),
692            }
693        );
694    }
695
696    #[test]
697    fn test_normalize_errno_override() {
698        let mut mechanism = Mechanism {
699            ty: Annotated::new("generic".to_owned()),
700            meta: Annotated::new(MechanismMeta {
701                errno: Annotated::new(CError {
702                    number: Annotated::new(2),
703                    name: Annotated::new("OVERRIDDEN".to_owned()),
704                }),
705                ..Default::default()
706            }),
707            ..Default::default()
708        };
709
710        normalize_mechanism(&mut mechanism, Some(OsHint::Linux));
711
712        let errno = mechanism.meta.value().unwrap().errno.value().unwrap();
713        assert_eq!(
714            errno,
715            &CError {
716                number: Annotated::new(2),
717                name: Annotated::new("OVERRIDDEN".to_owned()),
718            }
719        );
720    }
721
722    #[test]
723    fn test_normalize_errno_fail() {
724        let mut mechanism = Mechanism {
725            ty: Annotated::new("generic".to_owned()),
726            meta: Annotated::new(MechanismMeta {
727                errno: Annotated::new(CError {
728                    number: Annotated::new(2),
729                    ..Default::default()
730                }),
731                ..Default::default()
732            }),
733            ..Default::default()
734        };
735
736        normalize_mechanism(&mut mechanism, None);
737
738        let errno = mechanism.meta.value().unwrap().errno.value().unwrap();
739        assert_eq!(
740            errno,
741            &CError {
742                number: Annotated::new(2),
743                ..Default::default()
744            }
745        );
746    }
747
748    #[test]
749    fn test_normalize_signal() {
750        let mut mechanism = Mechanism {
751            ty: Annotated::new("generic".to_owned()),
752            meta: Annotated::new(MechanismMeta {
753                signal: Annotated::new(PosixSignal {
754                    number: Annotated::new(11),
755                    code: Annotated::new(0),
756                    ..Default::default()
757                }),
758                ..Default::default()
759            }),
760            ..Default::default()
761        };
762
763        normalize_mechanism(&mut mechanism, Some(OsHint::Darwin));
764
765        let signal = mechanism.meta.value().unwrap().signal.value().unwrap();
766        assert_eq!(
767            signal,
768            &PosixSignal {
769                number: Annotated::new(11),
770                code: Annotated::new(0),
771                name: Annotated::new("SIGSEGV".to_owned()),
772                code_name: Annotated::new("SEGV_NOOP".to_owned()),
773            }
774        );
775    }
776
777    #[test]
778    fn test_normalize_partial_signal() {
779        let mut mechanism = Mechanism {
780            ty: Annotated::new("generic".to_owned()),
781            meta: Annotated::new(MechanismMeta {
782                signal: Annotated::new(PosixSignal {
783                    number: Annotated::new(11),
784                    ..Default::default()
785                }),
786                ..Default::default()
787            }),
788            ..Default::default()
789        };
790
791        normalize_mechanism(&mut mechanism, Some(OsHint::Linux));
792
793        let signal = mechanism.meta.value().unwrap().signal.value().unwrap();
794
795        assert_eq!(
796            signal,
797            &PosixSignal {
798                number: Annotated::new(11),
799                name: Annotated::new("SIGSEGV".to_owned()),
800                ..Default::default()
801            }
802        );
803    }
804
805    #[test]
806    fn test_normalize_signal_override() {
807        let mut mechanism = Mechanism {
808            ty: Annotated::new("generic".to_owned()),
809            meta: Annotated::new(MechanismMeta {
810                signal: Annotated::new(PosixSignal {
811                    number: Annotated::new(11),
812                    code: Annotated::new(0),
813                    name: Annotated::new("OVERRIDDEN".to_owned()),
814                    code_name: Annotated::new("OVERRIDDEN".to_owned()),
815                }),
816                ..Default::default()
817            }),
818            ..Default::default()
819        };
820
821        normalize_mechanism(&mut mechanism, Some(OsHint::Linux));
822
823        let signal = mechanism.meta.value().unwrap().signal.value().unwrap();
824
825        assert_eq!(
826            signal,
827            &PosixSignal {
828                number: Annotated::new(11),
829                code: Annotated::new(0),
830                name: Annotated::new("OVERRIDDEN".to_owned()),
831                code_name: Annotated::new("OVERRIDDEN".to_owned()),
832            }
833        );
834    }
835
836    #[test]
837    fn test_normalize_signal_fail() {
838        let mut mechanism = Mechanism {
839            ty: Annotated::new("generic".to_owned()),
840            meta: Annotated::new(MechanismMeta {
841                signal: Annotated::new(PosixSignal {
842                    number: Annotated::new(11),
843                    code: Annotated::new(0),
844                    ..Default::default()
845                }),
846                ..Default::default()
847            }),
848            ..Default::default()
849        };
850
851        normalize_mechanism(&mut mechanism, None);
852
853        let signal = mechanism.meta.value().unwrap().signal.value().unwrap();
854
855        assert_eq!(
856            signal,
857            &PosixSignal {
858                number: Annotated::new(11),
859                code: Annotated::new(0),
860                ..Default::default()
861            }
862        );
863    }
864
865    #[test]
866    fn test_normalize_mach() {
867        let mut mechanism = Mechanism {
868            ty: Annotated::new("generic".to_owned()),
869            meta: Annotated::new(MechanismMeta {
870                mach_exception: Annotated::new(MachException {
871                    ty: Annotated::new(1),
872                    code: Annotated::new(1),
873                    subcode: Annotated::new(8),
874                    ..Default::default()
875                }),
876                ..Default::default()
877            }),
878            ..Default::default()
879        };
880
881        // We do not need SDK information here because mach exceptions only
882        // occur on Darwin
883
884        normalize_mechanism(&mut mechanism, None);
885
886        let mach_exception = mechanism
887            .meta
888            .value()
889            .unwrap()
890            .mach_exception
891            .value()
892            .unwrap();
893
894        assert_eq!(
895            mach_exception,
896            &MachException {
897                ty: Annotated::new(1),
898                code: Annotated::new(1),
899                subcode: Annotated::new(8),
900                name: Annotated::new("EXC_BAD_ACCESS".to_owned()),
901            }
902        );
903    }
904
905    #[test]
906    fn test_normalize_mach_override() {
907        let mut mechanism = Mechanism {
908            ty: Annotated::new("generic".to_owned()),
909            meta: Annotated::new(MechanismMeta {
910                mach_exception: Annotated::new(MachException {
911                    ty: Annotated::new(1),
912                    code: Annotated::new(1),
913                    subcode: Annotated::new(8),
914                    name: Annotated::new("OVERRIDE".to_owned()),
915                }),
916                ..Default::default()
917            }),
918            ..Default::default()
919        };
920
921        // We do not need SDK information here because mach exceptions only
922        // occur on Darwin
923
924        normalize_mechanism(&mut mechanism, None);
925
926        let mach_exception = mechanism
927            .meta
928            .value()
929            .unwrap()
930            .mach_exception
931            .value()
932            .unwrap();
933        assert_eq!(
934            mach_exception,
935            &MachException {
936                ty: Annotated::new(1),
937                code: Annotated::new(1),
938                subcode: Annotated::new(8),
939                name: Annotated::new("OVERRIDE".to_owned()),
940            }
941        );
942    }
943
944    #[test]
945    fn test_normalize_mach_fail() {
946        let mut mechanism = Mechanism {
947            ty: Annotated::new("generic".to_owned()),
948            meta: Annotated::new(MechanismMeta {
949                mach_exception: Annotated::new(MachException {
950                    ty: Annotated::new(99),
951                    code: Annotated::new(1),
952                    subcode: Annotated::new(8),
953                    ..Default::default()
954                }),
955                ..Default::default()
956            }),
957            ..Default::default()
958        };
959
960        // We do not need SDK information here because mach exceptions only
961        // occur on Darwin
962
963        normalize_mechanism(&mut mechanism, None);
964
965        let mach_exception = mechanism
966            .meta
967            .value()
968            .unwrap()
969            .mach_exception
970            .value()
971            .unwrap();
972        assert_eq!(
973            mach_exception,
974            &MachException {
975                ty: Annotated::new(99),
976                code: Annotated::new(1),
977                subcode: Annotated::new(8),
978                ..Default::default()
979            }
980        );
981    }
982
983    #[test]
984    fn test_normalize_http_url() {
985        let mut good_mechanism = Mechanism {
986            ty: Annotated::new("generic".to_owned()),
987            help_link: Annotated::new("https://example.com/".to_owned()),
988            ..Default::default()
989        };
990
991        normalize_mechanism(&mut good_mechanism, None);
992        insta::assert_ron_snapshot!(SerializableAnnotated(&Annotated::new(good_mechanism)), @r#"
993        {
994          "type": "generic",
995          "help_link": "https://example.com/",
996        }
997        "#);
998
999        let mut bad_mechanism = Mechanism {
1000            ty: Annotated::new("generic".to_owned()),
1001            help_link: Annotated::new("javascript:alert(document.cookie)".to_owned()),
1002            ..Default::default()
1003        };
1004
1005        normalize_mechanism(&mut bad_mechanism, None);
1006        insta::assert_ron_snapshot!(SerializableAnnotated(&Annotated::new(bad_mechanism)), @r#"
1007        {
1008          "type": "generic",
1009          "help_link": (),
1010          "_meta": {
1011            "help_link": {
1012              "": Meta(Some(MetaInner(
1013                err: [
1014                  [
1015                    "invalid_data",
1016                    {
1017                      "reason": "expected http URL",
1018                    },
1019                  ],
1020                ],
1021                val: Some("javascript:alert(document.cookie)"),
1022              ))),
1023            },
1024          },
1025        }
1026        "#);
1027    }
1028}