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            && let Some(sdk_info) = debug_meta.system_sdk.value()
575            && let Some(name) = sdk_info.sdk_name.as_str()
576        {
577            return Self::from_name(name);
578        }
579
580        if let Some(os_context) = event.context::<OsContext>()
581            && let Some(name) = os_context.name.as_str()
582        {
583            return Self::from_name(name);
584        }
585
586        None
587    }
588}
589
590/// Normalizes the exception mechanism in place.
591pub fn normalize_mechanism(mechanism: &mut Mechanism, os_hint: Option<OsHint>) {
592    let _ = processor::apply(&mut mechanism.help_link, |value, meta| {
593        if value.starts_with("http://") || value.starts_with("https://") {
594            Ok(())
595        } else {
596            meta.add_error(Error::expected("http URL"));
597            Err(ProcessingAction::DeleteValueSoft)
598        }
599    });
600
601    let meta = match mechanism.meta.value_mut() {
602        Some(meta) => meta,
603        None => return,
604    };
605
606    if let Some(os_hint) = os_hint {
607        if let Some(cerror) = meta.errno.value_mut()
608            && cerror.name.value().is_none()
609            && let Some(errno) = cerror.number.value()
610            && let Some(name) = get_errno_name(*errno, os_hint)
611        {
612            cerror.name = Annotated::new(name.to_owned());
613        }
614
615        if let Some(signal) = meta.signal.value_mut()
616            && let Some(signo) = signal.number.value()
617        {
618            if signal.name.value().is_none()
619                && let Some(name) = get_signal_name(*signo, os_hint)
620            {
621                signal.name = Annotated::new(name.to_owned());
622            }
623
624            if os_hint == OsHint::Darwin
625                && signal.code_name.value().is_none()
626                && let Some(code) = signal.code.value()
627                && let Some(code_name) = get_signal_code_name(*signo, *code)
628            {
629                signal.code_name = Annotated::new(code_name.to_owned());
630            }
631        }
632    }
633
634    if let Some(mach_exception) = meta.mach_exception.value_mut()
635        && let Some(number) = mach_exception.ty.value()
636        && mach_exception.name.value().is_none()
637        && let Some(name) = get_mach_exception_name(*number)
638    {
639        mach_exception.name = Annotated::new(name.to_owned());
640    }
641}
642
643#[cfg(test)]
644mod tests {
645    use relay_event_schema::protocol::{CError, MachException, MechanismMeta, PosixSignal};
646    use relay_protocol::SerializableAnnotated;
647    use similar_asserts::assert_eq;
648
649    use super::*;
650
651    #[test]
652    fn test_normalize_missing() {
653        let mut mechanism = Mechanism {
654            ty: Annotated::new("generic".to_owned()),
655            ..Default::default()
656        };
657
658        let old_mechanism = mechanism.clone();
659
660        normalize_mechanism(&mut mechanism, None);
661
662        assert_eq!(mechanism, old_mechanism);
663    }
664
665    #[test]
666    fn test_normalize_errno() {
667        let mut mechanism = Mechanism {
668            ty: Annotated::new("generic".to_owned()),
669            meta: Annotated::new(MechanismMeta {
670                errno: Annotated::new(CError {
671                    number: Annotated::new(2),
672                    ..Default::default()
673                }),
674                ..Default::default()
675            }),
676            ..Default::default()
677        };
678
679        normalize_mechanism(&mut mechanism, Some(OsHint::Linux));
680
681        let errno = mechanism.meta.value().unwrap().errno.value().unwrap();
682        assert_eq!(
683            errno,
684            &CError {
685                number: Annotated::new(2),
686                name: Annotated::new("ENOENT".to_owned()),
687            }
688        );
689    }
690
691    #[test]
692    fn test_normalize_errno_override() {
693        let mut mechanism = Mechanism {
694            ty: Annotated::new("generic".to_owned()),
695            meta: Annotated::new(MechanismMeta {
696                errno: Annotated::new(CError {
697                    number: Annotated::new(2),
698                    name: Annotated::new("OVERRIDDEN".to_owned()),
699                }),
700                ..Default::default()
701            }),
702            ..Default::default()
703        };
704
705        normalize_mechanism(&mut mechanism, Some(OsHint::Linux));
706
707        let errno = mechanism.meta.value().unwrap().errno.value().unwrap();
708        assert_eq!(
709            errno,
710            &CError {
711                number: Annotated::new(2),
712                name: Annotated::new("OVERRIDDEN".to_owned()),
713            }
714        );
715    }
716
717    #[test]
718    fn test_normalize_errno_fail() {
719        let mut mechanism = Mechanism {
720            ty: Annotated::new("generic".to_owned()),
721            meta: Annotated::new(MechanismMeta {
722                errno: Annotated::new(CError {
723                    number: Annotated::new(2),
724                    ..Default::default()
725                }),
726                ..Default::default()
727            }),
728            ..Default::default()
729        };
730
731        normalize_mechanism(&mut mechanism, None);
732
733        let errno = mechanism.meta.value().unwrap().errno.value().unwrap();
734        assert_eq!(
735            errno,
736            &CError {
737                number: Annotated::new(2),
738                ..Default::default()
739            }
740        );
741    }
742
743    #[test]
744    fn test_normalize_signal() {
745        let mut mechanism = Mechanism {
746            ty: Annotated::new("generic".to_owned()),
747            meta: Annotated::new(MechanismMeta {
748                signal: Annotated::new(PosixSignal {
749                    number: Annotated::new(11),
750                    code: Annotated::new(0),
751                    ..Default::default()
752                }),
753                ..Default::default()
754            }),
755            ..Default::default()
756        };
757
758        normalize_mechanism(&mut mechanism, Some(OsHint::Darwin));
759
760        let signal = mechanism.meta.value().unwrap().signal.value().unwrap();
761        assert_eq!(
762            signal,
763            &PosixSignal {
764                number: Annotated::new(11),
765                code: Annotated::new(0),
766                name: Annotated::new("SIGSEGV".to_owned()),
767                code_name: Annotated::new("SEGV_NOOP".to_owned()),
768            }
769        );
770    }
771
772    #[test]
773    fn test_normalize_partial_signal() {
774        let mut mechanism = Mechanism {
775            ty: Annotated::new("generic".to_owned()),
776            meta: Annotated::new(MechanismMeta {
777                signal: Annotated::new(PosixSignal {
778                    number: Annotated::new(11),
779                    ..Default::default()
780                }),
781                ..Default::default()
782            }),
783            ..Default::default()
784        };
785
786        normalize_mechanism(&mut mechanism, Some(OsHint::Linux));
787
788        let signal = mechanism.meta.value().unwrap().signal.value().unwrap();
789
790        assert_eq!(
791            signal,
792            &PosixSignal {
793                number: Annotated::new(11),
794                name: Annotated::new("SIGSEGV".to_owned()),
795                ..Default::default()
796            }
797        );
798    }
799
800    #[test]
801    fn test_normalize_signal_override() {
802        let mut mechanism = Mechanism {
803            ty: Annotated::new("generic".to_owned()),
804            meta: Annotated::new(MechanismMeta {
805                signal: Annotated::new(PosixSignal {
806                    number: Annotated::new(11),
807                    code: Annotated::new(0),
808                    name: Annotated::new("OVERRIDDEN".to_owned()),
809                    code_name: Annotated::new("OVERRIDDEN".to_owned()),
810                }),
811                ..Default::default()
812            }),
813            ..Default::default()
814        };
815
816        normalize_mechanism(&mut mechanism, Some(OsHint::Linux));
817
818        let signal = mechanism.meta.value().unwrap().signal.value().unwrap();
819
820        assert_eq!(
821            signal,
822            &PosixSignal {
823                number: Annotated::new(11),
824                code: Annotated::new(0),
825                name: Annotated::new("OVERRIDDEN".to_owned()),
826                code_name: Annotated::new("OVERRIDDEN".to_owned()),
827            }
828        );
829    }
830
831    #[test]
832    fn test_normalize_signal_fail() {
833        let mut mechanism = Mechanism {
834            ty: Annotated::new("generic".to_owned()),
835            meta: Annotated::new(MechanismMeta {
836                signal: Annotated::new(PosixSignal {
837                    number: Annotated::new(11),
838                    code: Annotated::new(0),
839                    ..Default::default()
840                }),
841                ..Default::default()
842            }),
843            ..Default::default()
844        };
845
846        normalize_mechanism(&mut mechanism, None);
847
848        let signal = mechanism.meta.value().unwrap().signal.value().unwrap();
849
850        assert_eq!(
851            signal,
852            &PosixSignal {
853                number: Annotated::new(11),
854                code: Annotated::new(0),
855                ..Default::default()
856            }
857        );
858    }
859
860    #[test]
861    fn test_normalize_mach() {
862        let mut mechanism = Mechanism {
863            ty: Annotated::new("generic".to_owned()),
864            meta: Annotated::new(MechanismMeta {
865                mach_exception: Annotated::new(MachException {
866                    ty: Annotated::new(1),
867                    code: Annotated::new(1),
868                    subcode: Annotated::new(8),
869                    ..Default::default()
870                }),
871                ..Default::default()
872            }),
873            ..Default::default()
874        };
875
876        // We do not need SDK information here because mach exceptions only
877        // occur on Darwin
878
879        normalize_mechanism(&mut mechanism, None);
880
881        let mach_exception = mechanism
882            .meta
883            .value()
884            .unwrap()
885            .mach_exception
886            .value()
887            .unwrap();
888
889        assert_eq!(
890            mach_exception,
891            &MachException {
892                ty: Annotated::new(1),
893                code: Annotated::new(1),
894                subcode: Annotated::new(8),
895                name: Annotated::new("EXC_BAD_ACCESS".to_owned()),
896            }
897        );
898    }
899
900    #[test]
901    fn test_normalize_mach_override() {
902        let mut mechanism = Mechanism {
903            ty: Annotated::new("generic".to_owned()),
904            meta: Annotated::new(MechanismMeta {
905                mach_exception: Annotated::new(MachException {
906                    ty: Annotated::new(1),
907                    code: Annotated::new(1),
908                    subcode: Annotated::new(8),
909                    name: Annotated::new("OVERRIDE".to_owned()),
910                }),
911                ..Default::default()
912            }),
913            ..Default::default()
914        };
915
916        // We do not need SDK information here because mach exceptions only
917        // occur on Darwin
918
919        normalize_mechanism(&mut mechanism, None);
920
921        let mach_exception = mechanism
922            .meta
923            .value()
924            .unwrap()
925            .mach_exception
926            .value()
927            .unwrap();
928        assert_eq!(
929            mach_exception,
930            &MachException {
931                ty: Annotated::new(1),
932                code: Annotated::new(1),
933                subcode: Annotated::new(8),
934                name: Annotated::new("OVERRIDE".to_owned()),
935            }
936        );
937    }
938
939    #[test]
940    fn test_normalize_mach_fail() {
941        let mut mechanism = Mechanism {
942            ty: Annotated::new("generic".to_owned()),
943            meta: Annotated::new(MechanismMeta {
944                mach_exception: Annotated::new(MachException {
945                    ty: Annotated::new(99),
946                    code: Annotated::new(1),
947                    subcode: Annotated::new(8),
948                    ..Default::default()
949                }),
950                ..Default::default()
951            }),
952            ..Default::default()
953        };
954
955        // We do not need SDK information here because mach exceptions only
956        // occur on Darwin
957
958        normalize_mechanism(&mut mechanism, None);
959
960        let mach_exception = mechanism
961            .meta
962            .value()
963            .unwrap()
964            .mach_exception
965            .value()
966            .unwrap();
967        assert_eq!(
968            mach_exception,
969            &MachException {
970                ty: Annotated::new(99),
971                code: Annotated::new(1),
972                subcode: Annotated::new(8),
973                ..Default::default()
974            }
975        );
976    }
977
978    #[test]
979    fn test_normalize_http_url() {
980        let mut good_mechanism = Mechanism {
981            ty: Annotated::new("generic".to_owned()),
982            help_link: Annotated::new("https://example.com/".to_owned()),
983            ..Default::default()
984        };
985
986        normalize_mechanism(&mut good_mechanism, None);
987        insta::assert_ron_snapshot!(SerializableAnnotated(&Annotated::new(good_mechanism)), @r###"
988        {
989          "type": "generic",
990          "help_link": "https://example.com/",
991        }
992        "###);
993
994        let mut bad_mechanism = Mechanism {
995            ty: Annotated::new("generic".to_owned()),
996            help_link: Annotated::new("javascript:alert(document.cookie)".to_owned()),
997            ..Default::default()
998        };
999
1000        normalize_mechanism(&mut bad_mechanism, None);
1001        insta::assert_ron_snapshot!(SerializableAnnotated(&Annotated::new(bad_mechanism)), @r###"
1002        {
1003          "type": "generic",
1004          "help_link": (),
1005          "_meta": {
1006            "help_link": {
1007              "": Meta(Some(MetaInner(
1008                err: [
1009                  [
1010                    "invalid_data",
1011                    {
1012                      "reason": "expected http URL",
1013                    },
1014                  ],
1015                ],
1016                val: Some("javascript:alert(document.cookie)"),
1017              ))),
1018            },
1019          },
1020        }
1021        "###);
1022    }
1023}