1 | // SPDX-License-Identifier: GPL-3.0-or-later |
2 | |
3 | #include <dirent.h> |
4 | #include <errno.h> |
5 | #include <fcntl.h> |
6 | #include <readline/libreadline.h> |
7 | #include <stdio.h> |
8 | #include <stdlib.h> |
9 | #include <string.h> |
10 | #include <unistd.h> |
11 | |
12 | #define BUFSIZE 4096 |
13 | |
14 | void clear_console(void) |
15 | { |
16 | printf(format: "\033[2J\033[1;1H" ); |
17 | } |
18 | |
19 | static void print_file(FILE *f) |
20 | { |
21 | do |
22 | { |
23 | char buffer[BUFSIZE] = { 0 }; |
24 | size_t sz = fread(buffer, size: 1, BUFSIZE, stream: f); |
25 | if (sz == 0 || sz == (size_t) -1) |
26 | break; |
27 | |
28 | fwrite(buffer, size: 1, count: sz, stream: stdout); |
29 | } while (true); |
30 | } |
31 | |
32 | static void open_and_print_file(const char *path) |
33 | { |
34 | FILE *f = fopen(filename: path, mode: "r" ); |
35 | if (!f) |
36 | { |
37 | fprintf(stream: stderr, format: "failed to open file '%s'\n" , path); |
38 | return; |
39 | } |
40 | |
41 | print_file(f); |
42 | fclose(stream: f); |
43 | } |
44 | static void do_prompt_rw(const char *prompt, const char *filename) |
45 | { |
46 | while (true) |
47 | { |
48 | char *line = readline(prompt); |
49 | if (!line || strlen(s: line) == 0) |
50 | { |
51 | puts(string: "leaving..." ); |
52 | if (line) |
53 | free(pointer: line); |
54 | return; |
55 | } |
56 | |
57 | FILE *f = fopen(filename, mode: "r+" ); |
58 | setbuf(stream: f, NULL); |
59 | if (!f) |
60 | { |
61 | fprintf(stream: stderr, format: "failed to open '%s'\n" , filename); |
62 | free(pointer: line); |
63 | return; |
64 | } |
65 | |
66 | fprintf(stream: f, format: "%s" , line); |
67 | print_file(f); |
68 | fclose(stream: f); |
69 | free(pointer: line); |
70 | } |
71 | } |
72 | |
73 | static void do_memstat(void) |
74 | { |
75 | open_and_print_file(path: "/sys/mmstat/stat" ); |
76 | } |
77 | |
78 | static void do_leave(void) |
79 | { |
80 | exit(status: 0); |
81 | } |
82 | |
83 | static FILE *open_debug_file(const char *module, const char *mode) |
84 | { |
85 | char *path; |
86 | asprintf(&path, "/sys/debug/%s" , module); |
87 | FILE *file = fopen(filename: path, mode); |
88 | if (!file) |
89 | fprintf(stream: stderr, format: "failed to open '%s'\n" , path); |
90 | free(pointer: path); |
91 | return file; |
92 | } |
93 | |
94 | static bool get_debug_value(const char *module) |
95 | { |
96 | FILE *file = open_debug_file(module, mode: "r" ); |
97 | if (!file) |
98 | { |
99 | printf(format: "debug: cannot open '%s': %s\n" , module, strerror(errno)); |
100 | exit(status: 1); |
101 | } |
102 | |
103 | int value; |
104 | fscanf(stream: file, format: "%d" , &value); |
105 | fclose(stream: file); |
106 | return value; |
107 | } |
108 | |
109 | static bool set_debug_value(const char *module, bool value) |
110 | { |
111 | FILE *file = open_debug_file(module, mode: "w" ); |
112 | if (!file) |
113 | { |
114 | printf(format: "debug: cannot open '%s': %s\n" , module, strerror(errno)); |
115 | return false; |
116 | } |
117 | |
118 | fprintf(stream: file, format: "%d" , value); |
119 | fclose(stream: file); |
120 | return true; |
121 | } |
122 | |
123 | static void do_help(void); |
124 | |
125 | static void do_pagetable() |
126 | { |
127 | do_prompt_rw(prompt: "pid: " , filename: "/sys/mmstat/pagetable" ); |
128 | } |
129 | |
130 | static void do_vmaps() |
131 | { |
132 | do_prompt_rw(prompt: "pid: " , filename: "/sys/mmstat/vmaps" ); |
133 | } |
134 | |
135 | static void do_pstat(void) |
136 | { |
137 | do_prompt_rw(prompt: "pfn: " , filename: "/sys/mmstat/phyframe_stat" ); |
138 | } |
139 | |
140 | const struct |
141 | { |
142 | const char *name; |
143 | void (*func)(void); |
144 | } actions[] = { |
145 | { "memstat" , do_memstat }, // |
146 | { "q" , do_leave }, // |
147 | { "leave" , do_leave }, // |
148 | { "pstat" , do_pstat }, // |
149 | { "pagetable" , do_pagetable }, // |
150 | { "vmaps" , do_vmaps }, // |
151 | { "help" , do_help }, // |
152 | { "h" , do_help }, // |
153 | { NULL, NULL }, // |
154 | }; |
155 | |
156 | void do_help() |
157 | { |
158 | puts(string: "KD, the MOS kernel debugger." ); |
159 | puts(string: "Available commands:" ); |
160 | for (int i = 0; actions[i].name; i++) |
161 | printf(format: " %s\n" , actions[i].name); |
162 | |
163 | puts(string: "" ); |
164 | puts(string: "Also, you can use 'kd' to enable/disable debug modules." ); |
165 | puts(string: "Usage:" ); |
166 | puts(string: " kd -l [<module>] list all, or get the status of a debug module" ); |
167 | puts(string: " kd <module> <on|off> enable/disable a debug module" ); |
168 | } |
169 | |
170 | int main(int argc, const char *argv[]) |
171 | { |
172 | if (argc == 2 && strcmp(a: argv[1], b: "-h" ) == 0) |
173 | do_help(); |
174 | |
175 | if (argc == 2 && strcmp(a: argv[1], b: "-l" ) == 0) |
176 | { |
177 | // list all debug modules and their status |
178 | DIR *dir = opendir("/sys/debug" ); |
179 | if (!dir) |
180 | { |
181 | printf(format: "debug: cannot open '/sys/debug': %s\n" , strerror(errno)); |
182 | return 1; |
183 | } |
184 | |
185 | struct dirent *entry; |
186 | while ((entry = readdir(dir))) |
187 | { |
188 | if (entry->d_type != DT_REG) |
189 | continue; |
190 | |
191 | const bool on = get_debug_value(module: entry->d_name); |
192 | printf(format: "%s: %s\n" , entry->d_name, on ? "on" : "off" ); |
193 | } |
194 | |
195 | closedir(dir); |
196 | return 0; |
197 | } |
198 | |
199 | if (argc == 3) |
200 | { |
201 | if (strcmp(a: argv[1], b: "-l" ) == 0) |
202 | { |
203 | const char *module = argv[2]; |
204 | const bool on = get_debug_value(module); |
205 | printf(format: "%s: %s\n" , module, on ? "on" : "off" ); |
206 | } |
207 | else |
208 | { |
209 | const char *module = argv[1], *value = argv[2]; |
210 | |
211 | if (strcmp(a: value, b: "on" ) == 0 || strcmp(a: value, b: "1" ) == 0) |
212 | set_debug_value(module, value: true); |
213 | else if (strcmp(a: value, b: "off" ) == 0 || strcmp(a: value, b: "0" ) == 0) |
214 | set_debug_value(module, value: false); |
215 | else |
216 | printf(format: "debug: invalid value '%s'\n" , value); |
217 | } |
218 | return 0; |
219 | } |
220 | |
221 | // interactive mode |
222 | while (true) |
223 | { |
224 | const char *line = readline(prompt: "kd> " ); |
225 | if (!line) |
226 | break; |
227 | |
228 | if (strlen(s: line) == 0) |
229 | { |
230 | free(pointer: (void *) line); |
231 | continue; |
232 | } |
233 | |
234 | bool handled = false; |
235 | for (int i = 0; actions[i].name; i++) |
236 | { |
237 | if (!strcmp(a: line, b: actions[i].name)) |
238 | { |
239 | handled = true; |
240 | actions[i].func(); |
241 | free(pointer: (void *) line); |
242 | break; |
243 | } |
244 | } |
245 | |
246 | if (!handled) |
247 | { |
248 | if (strlen(s: line)) |
249 | puts(string: "unknown command" ); |
250 | free(pointer: (void *) line); |
251 | } |
252 | } |
253 | |
254 | return 0; |
255 | } |
256 | |