| 1 | // SPDX-License-Identifier: GPL-3.0-or-later |
| 2 | |
| 3 | #include "mos/assert.hpp" |
| 4 | #include "mos/misc/kallsyms.hpp" |
| 5 | #include "mos/platform/platform.hpp" |
| 6 | #include "mos/tasks/task_types.hpp" |
| 7 | |
| 8 | #include <mos/types.hpp> |
| 9 | #include <mos_stdio.hpp> |
| 10 | |
| 11 | static size_t do_print_vmflags(char *buf, size_t size, VMFlags flags) |
| 12 | { |
| 13 | return snprintf(str: buf, size, format: "%c%c%c" , flags.test(b: VM_READ) ? 'r' : '-', flags.test(b: VM_WRITE) ? 'w' : '-', flags.test(b: VM_EXEC) ? 'x' : '-'); |
| 14 | } |
| 15 | |
| 16 | /** |
| 17 | * @brief Kernel's extension to vsnprintf, '%p' format specifier. |
| 18 | * |
| 19 | * @details Supported extensions are listed below: |
| 20 | * |
| 21 | * '%ps' prints a kernel symbol name, with offset if applicable. |
| 22 | * e.g. "do_fork (+0x123)" |
| 23 | * '%pt' prints a thread_t object. |
| 24 | * e.g.: "[p123:my_process]" |
| 25 | * '%pp' prints a process_t object. |
| 26 | * e.g.: "[t123:my_thread]" |
| 27 | * '%pvf' prints VMFlags_t flag, only the r/w/x bits are printed for general purpose. |
| 28 | * e.g.: "rwx" / "r--" / "rw-" / "--x" |
| 29 | * '%pvm' prints a vmap_t object. |
| 30 | * e.g.: "{ 0x123000-0x123fff, rwx, on_fault=0x12345678 }" |
| 31 | * '%pio' prints an IO object. |
| 32 | * e.g.: "{ 'file.txt', offset=0x12345678 }" |
| 33 | * |
| 34 | * @returns true if the format specifier is handled, false otherwise, the |
| 35 | * caller should fallback to the default implementation. (i.e. %p) |
| 36 | * |
| 37 | */ |
| 38 | |
| 39 | size_t vsnprintf_do_pointer_kernel(char *buf, size_t *size, const char **pformat, ptr_t ptr) |
| 40 | { |
| 41 | size_t ret = 0; |
| 42 | #define current (**pformat) |
| 43 | #define peek_next (*(*pformat + 1)) |
| 44 | #define shift_next ((void) ((*pformat)++)) |
| 45 | #define unshift_next ((void) ((*pformat)--)) |
| 46 | |
| 47 | #define wrap_print(...) wrap_printed(snprintf(buf, *size, __VA_ARGS__)) |
| 48 | #define wrap_printed(x) \ |
| 49 | do \ |
| 50 | { \ |
| 51 | const size_t _printed = x; \ |
| 52 | buf += _printed, ret += _printed; \ |
| 53 | } while (0) |
| 54 | |
| 55 | if (current != 'p') |
| 56 | goto done; |
| 57 | |
| 58 | #define null_check() \ |
| 59 | do \ |
| 60 | { \ |
| 61 | if (unlikely(ptr == 0)) \ |
| 62 | { \ |
| 63 | wrap_print("(null)"); \ |
| 64 | goto done; \ |
| 65 | } \ |
| 66 | } while (0) |
| 67 | |
| 68 | switch (peek_next) |
| 69 | { |
| 70 | case 'i': |
| 71 | { |
| 72 | shift_next; |
| 73 | switch (peek_next) |
| 74 | { |
| 75 | case 'o': |
| 76 | { |
| 77 | shift_next; |
| 78 | // IO |
| 79 | const IO *io = (const IO *) ptr; |
| 80 | if (io == NULL) |
| 81 | { |
| 82 | wrap_print("(null)" ); |
| 83 | goto done; |
| 84 | } |
| 85 | |
| 86 | const auto name = io->name(); |
| 87 | wrap_print("{ '%s'" , name.c_str()); |
| 88 | if (!IO::IsValid(io)) |
| 89 | wrap_print(", invalid" ); |
| 90 | wrap_print(" }" ); |
| 91 | goto done; |
| 92 | } |
| 93 | default: return false; |
| 94 | } |
| 95 | |
| 96 | MOS_UNREACHABLE(); |
| 97 | } |
| 98 | case 's': // %ps |
| 99 | { |
| 100 | shift_next; |
| 101 | // kernel symbol address |
| 102 | const kallsyms_t *sym = kallsyms_get_symbol(addr: ptr); |
| 103 | if (!sym) |
| 104 | { |
| 105 | wrap_print("(unknown)" ); |
| 106 | goto done; |
| 107 | } |
| 108 | |
| 109 | const off_t off = ptr - sym->address; |
| 110 | if (off) |
| 111 | wrap_print("%s (+0x%zx)" , sym ? sym->name : "(unknown)" , off); |
| 112 | else |
| 113 | wrap_print("%s" , sym ? sym->name : "(unknown)" ); |
| 114 | goto done; |
| 115 | } |
| 116 | case 't': // %pt |
| 117 | { |
| 118 | shift_next; |
| 119 | // thread_t |
| 120 | null_check(); |
| 121 | const auto thread = (Thread *) ptr; |
| 122 | if (thread == NULL) |
| 123 | { |
| 124 | wrap_print("(null)" ); |
| 125 | goto done; |
| 126 | } |
| 127 | MOS_ASSERT_X(Thread::IsValid(thread), "thread is invalid" ); |
| 128 | wrap_print("[t%d:%s]" , thread->tid, thread->name.empty() ? "<no name>" : thread->name.data()); |
| 129 | goto done; |
| 130 | } |
| 131 | case 'p': // %pp |
| 132 | { |
| 133 | shift_next; |
| 134 | // process_t |
| 135 | null_check(); |
| 136 | const Process *process = (const Process *) ptr; |
| 137 | if (process == NULL) |
| 138 | { |
| 139 | wrap_print("(null)" ); |
| 140 | goto done; |
| 141 | } |
| 142 | MOS_ASSERT_X(Process::IsValid(process), "process is invalid" ); |
| 143 | wrap_print("[p%d:%s]" , process->pid, process->name.empty() ? "<no name>" : process->name.data()); |
| 144 | goto done; |
| 145 | } |
| 146 | case 'v': // %pv |
| 147 | { |
| 148 | shift_next; |
| 149 | |
| 150 | // vmap-related types |
| 151 | switch (peek_next) |
| 152 | { |
| 153 | case 'f': // %pvf |
| 154 | { |
| 155 | shift_next; |
| 156 | // VMFlags, only r/w/x are supported |
| 157 | const VMFlags flags = *(VMFlags *) ptr; |
| 158 | wrap_printed(do_print_vmflags(buf, *size, flags)); |
| 159 | goto done; |
| 160 | } |
| 161 | case 'm': // %pvm |
| 162 | { |
| 163 | shift_next; |
| 164 | // vmap_t *, print the vmap's range and flags |
| 165 | const vmap_t *vmap = (const vmap_t *) ptr; |
| 166 | if (vmap == NULL) |
| 167 | { |
| 168 | wrap_print("(null)" ); |
| 169 | goto done; |
| 170 | } |
| 171 | |
| 172 | wrap_print("{ [" PTR_VLFMT " - " PTR_VLFMT "], " , vmap->vaddr, vmap->vaddr + vmap->npages * MOS_PAGE_SIZE - 1); |
| 173 | wrap_printed(do_print_vmflags(buf, *size, vmap->vmflags)); |
| 174 | wrap_print(", fault: %ps" , (void *) (ptr_t) vmap->on_fault); |
| 175 | |
| 176 | if (vmap->io) |
| 177 | { |
| 178 | const auto name = vmap->io->name(); |
| 179 | wrap_print(", io: '%s', offset: 0x%zx" , name.c_str(), vmap->io_offset); |
| 180 | } |
| 181 | |
| 182 | wrap_print(" }" ); |
| 183 | goto done; |
| 184 | } |
| 185 | default: return false; |
| 186 | } |
| 187 | } |
| 188 | default: return false; |
| 189 | } |
| 190 | |
| 191 | done: |
| 192 | return ret; |
| 193 | } |
| 194 | |