xref: /haiku/src/system/kernel/debug/debug_builtin_commands.cpp (revision 0ce4c23d22fae64d10e5575687490fbdf8ee52b8)
1 /*
2  * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de
3  * Copyright 2002-2008, Axel Dörfler, axeld@pinc-software.de
4  * Distributed under the terms of the MIT License.
5  *
6  * Copyright 2001, Travis Geiselbrecht. All rights reserved.
7  * Distributed under the terms of the NewOS License.
8  */
9 
10 #include "debug_builtin_commands.h"
11 
12 #include <ctype.h>
13 #include <string.h>
14 
15 #include <debug.h>
16 #include <kernel.h>
17 
18 #include "debug_commands.h"
19 #include "gdb.h"
20 
21 
22 static int
23 cmd_reboot(int argc, char **argv)
24 {
25 	arch_cpu_shutdown(true);
26 	return 0;
27 		// I'll be really suprised if this line ever runs! ;-)
28 }
29 
30 
31 static int
32 cmd_shutdown(int argc, char **argv)
33 {
34 	arch_cpu_shutdown(false);
35 	return 0;
36 }
37 
38 
39 static int
40 cmd_help(int argc, char **argv)
41 {
42 	debugger_command *command, *specified = NULL;
43 	const char *start = NULL;
44 	int32 startLength = 0;
45 	bool ambiguous;
46 
47 	if (argc > 1) {
48 		specified = find_debugger_command(argv[1], false, ambiguous);
49 		if (specified == NULL) {
50 			start = argv[1];
51 			startLength = strlen(start);
52 		}
53 	}
54 
55 	if (specified != NULL) {
56 		// only print out the help of the specified command (and all of its aliases)
57 		kprintf("debugger command for \"%s\" and aliases:\n", specified->name);
58 	} else if (start != NULL)
59 		kprintf("debugger commands starting with \"%s\":\n", start);
60 	else
61 		kprintf("debugger commands:\n");
62 
63 	for (command = get_debugger_commands(); command != NULL;
64 			command = command->next) {
65 		if (specified && command->func != specified->func)
66 			continue;
67 		if (start != NULL && strncmp(start, command->name, startLength))
68 			continue;
69 
70 		kprintf(" %-20s\t\t%s\n", command->name, command->description ? command->description : "-");
71 	}
72 
73 	return 0;
74 }
75 
76 
77 static int
78 cmd_continue(int argc, char **argv)
79 {
80 	return B_KDEBUG_QUIT;
81 }
82 
83 
84 static int
85 cmd_expr(int argc, char **argv)
86 {
87 	if (argc != 2) {
88 		print_debugger_command_usage(argv[0]);
89 		return 0;
90 	}
91 
92 	uint64 result;
93 	if (evaluate_debug_expression(argv[1], &result, false)) {
94 		kprintf("%" B_PRIu64 " (0x%" B_PRIx64 ")\n", result, result);
95 		set_debug_variable("_", result);
96 	}
97 
98 	return 0;
99 }
100 
101 
102 static int
103 cmd_error(int argc, char **argv)
104 {
105 	if (argc != 2) {
106 		print_debugger_command_usage(argv[0]);
107 		return 0;
108 	}
109 
110 	int32 error = parse_expression(argv[1]);
111 	kprintf("error 0x%" B_PRIx32 ": %s\n", error, strerror(error));
112 
113 	return 0;
114 }
115 
116 
117 static int
118 cmd_head(int argc, char** argv)
119 {
120 	debugger_command_pipe_segment* segment
121 		= get_current_debugger_command_pipe_segment();
122 	if (segment == NULL) {
123 		kprintf_unfiltered("%s can only be run as part of a pipe!\n", argv[0]);
124 		return B_KDEBUG_ERROR;
125 	}
126 
127 	struct user_data {
128 		uint64	max_lines;
129 		uint64	lines;
130 	};
131 	user_data* userData = (user_data*)segment->user_data;
132 
133 	if (segment->invocations == 0) {
134 		if (argc != 3) {
135 			print_debugger_command_usage(argv[0]);
136 			return B_KDEBUG_ERROR;
137 		}
138 
139 		if (!evaluate_debug_expression(argv[1], &userData->max_lines, false))
140 			return B_KDEBUG_ERROR;
141 		userData->lines = 0;
142 	}
143 
144 	if (++userData->lines <= userData->max_lines) {
145 		kputs(argv[2]);
146 		kputs("\n");
147 	}
148 
149 	return 0;
150 }
151 
152 
153 static int
154 cmd_tail(int argc, char** argv)
155 {
156 	debugger_command_pipe_segment* segment
157 		= get_current_debugger_command_pipe_segment();
158 	if (segment == NULL) {
159 		kprintf_unfiltered("%s can only be run as part of a pipe!\n", argv[0]);
160 		return B_KDEBUG_ERROR;
161 	}
162 
163 	struct user_data {
164 		uint64	max_lines;
165 		int64	line_count;
166 		bool	restarted;
167 	};
168 	user_data* userData = (user_data*)segment->user_data;
169 
170 	if (segment->invocations == 0) {
171 		if (argc > 3) {
172 			print_debugger_command_usage(argv[0]);
173 			return B_KDEBUG_ERROR;
174 		}
175 
176 		userData->max_lines = 10;
177 		if (argc > 2 && !evaluate_debug_expression(argv[1],
178 				&userData->max_lines, false)) {
179 			return B_KDEBUG_ERROR;
180 		}
181 
182 		userData->line_count = 1;
183 		userData->restarted = false;
184 	} else if (!userData->restarted) {
185 		if (argv[argc - 1] == NULL) {
186 			userData->restarted = true;
187 			userData->line_count -= userData->max_lines;
188 			return B_KDEBUG_RESTART_PIPE;
189 		}
190 
191 		++userData->line_count;
192 	} else {
193 		if (argv[argc - 1] == NULL)
194 			return 0;
195 
196 		if (--userData->line_count < 0) {
197 			kputs(argv[argc - 1]);
198 			kputs("\n");
199 		}
200 	}
201 
202 	return 0;
203 }
204 
205 
206 static int
207 cmd_grep(int argc, char** argv)
208 {
209 	bool caseSensitive = true;
210 	bool inverseMatch = false;
211 
212 	int argi = 1;
213 	for (; argi < argc; argi++) {
214 		const char* arg = argv[argi];
215 		if (arg[0] != '-')
216 			break;
217 
218 		for (int32 i = 1; arg[i] != '\0'; i++) {
219 			if (arg[i] == 'i') {
220 				caseSensitive = false;
221 			} else if (arg[i] == 'v') {
222 				inverseMatch = true;
223 			} else {
224 				print_debugger_command_usage(argv[0]);
225 				return B_KDEBUG_ERROR;
226 			}
227 		}
228 	}
229 
230 	if (argc - argi != 2) {
231 		print_debugger_command_usage(argv[0]);
232 		return B_KDEBUG_ERROR;
233 	}
234 
235 	const char* pattern = argv[argi++];
236 	const char* line = argv[argi++];
237 
238 	bool match;
239 	if (caseSensitive) {
240 		match = strstr(line, pattern) != NULL;
241 	} else {
242 		match = false;
243 		int32 lineLen = strlen(line);
244 		int32 patternLen = strlen(pattern);
245 		for (int32 i = 0; i <= lineLen - patternLen; i++) {
246 			// This is rather slow, but should be OK for our purposes.
247 			if (strncasecmp(line + i, pattern, patternLen) == 0) {
248 				match = true;
249 				break;
250 			}
251 		}
252 	}
253 
254 	if (match != inverseMatch) {
255 		kputs(line);
256 		kputs("\n");
257 	}
258 
259 	return 0;
260 }
261 
262 
263 static int
264 cmd_wc(int argc, char** argv)
265 {
266 	debugger_command_pipe_segment* segment
267 		= get_current_debugger_command_pipe_segment();
268 	if (segment == NULL) {
269 		kprintf_unfiltered("%s can only be run as part of a pipe!\n", argv[0]);
270 		return B_KDEBUG_ERROR;
271 	}
272 
273 	struct user_data {
274 		uint64	lines;
275 		uint64	words;
276 		uint64	chars;
277 	};
278 	user_data* userData = (user_data*)segment->user_data;
279 
280 	if (segment->invocations == 0) {
281 		if (argc != 2) {
282 			print_debugger_command_usage(argv[0]);
283 			return B_KDEBUG_ERROR;
284 		}
285 
286 		userData->lines = 0;
287 		userData->words = 0;
288 		userData->chars = 0;
289 	}
290 
291 	const char* line = argv[1];
292 	if (line == NULL) {
293 		// last run -- print results
294 		kprintf("%10" B_PRIu64 " %10" B_PRIu64 " %10" B_PRIu64 "\n",
295 			userData->lines, userData->words, userData->chars);
296 		return 0;
297 	}
298 
299 	userData->lines++;
300 	userData->chars++;
301 		// newline
302 
303 	// count words and chars in this line
304 	bool inWord = false;
305 	for (; *line != '\0'; line++) {
306 		userData->chars++;
307 		if ((isspace(*line) != 0) == inWord) {
308 			inWord = !inWord;
309 			if (inWord)
310 				userData->words++;
311 		}
312 	}
313 
314 	return 0;
315 }
316 
317 
318 static int
319 cmd_faults(int argc, char** argv)
320 {
321 	if (argc > 2) {
322 		print_debugger_command_usage(argv[0]);
323 		return B_KDEBUG_ERROR;
324 	}
325 
326 	if (argc == 2)
327 		gInvokeCommandDirectly = parse_expression(argv[1]) == 0;
328 
329 	kprintf("Fault handling is %s%s.\n", argc == 2 ? "now " : "",
330 		gInvokeCommandDirectly ? "off" : "on");
331 	return 0;
332 }
333 
334 
335 // #pragma mark -
336 
337 
338 void
339 debug_builtin_commands_init()
340 {
341 	add_debugger_command_etc("help", &cmd_help, "List all debugger commands",
342 		"[name]\n"
343 		"Lists all debugger commands or those starting with \"name\".\n", 0);
344 	add_debugger_command_etc("reboot", &cmd_reboot, "Reboot the system",
345 		"\n"
346 		"Reboots the system.\n", 0);
347 	add_debugger_command_etc("shutdown", &cmd_shutdown, "Shut down the system",
348 		"\n"
349 		"Shuts down the system.\n", 0);
350 	add_debugger_command_etc("gdb", &cmd_gdb, "Connect to remote gdb",
351 		"\n"
352 		"Connects to a remote gdb connected to the serial port.\n", 0);
353 	add_debugger_command_etc("continue", &cmd_continue, "Leave kernel debugger",
354 		"\n"
355 		"Leaves kernel debugger.\n", 0);
356 	add_debugger_command_alias("exit", "continue", "Same as \"continue\"");
357 	add_debugger_command_alias("es", "continue", "Same as \"continue\"");
358 	add_debugger_command_etc("expr", &cmd_expr,
359 		"Evaluates the given expression and prints the result",
360 		"<expression>\n"
361 		"Evaluates the given expression and prints the result.\n",
362 		B_KDEBUG_DONT_PARSE_ARGUMENTS);
363 	add_debugger_command_etc("error", &cmd_error,
364 		"Prints a human-readable description for an error code",
365 		"<error>\n"
366 		"Prints a human-readable description for the given numeric error\n"
367 		"code.\n"
368 		"  <error>  - The numeric error code.\n", 0);
369 	add_debugger_command_etc("faults", &cmd_faults, "Toggles fault handling "
370 		"for debugger commands",
371 		"[0|1]\n"
372 		"Toggles fault handling on (1) or off (0).\n", 0);
373 	add_debugger_command_etc("head", &cmd_head,
374 		"Prints only the first lines of output from another command",
375 		"<maxLines>\n"
376 		"Should be used in a command pipe. It prints only the first\n"
377 		"<maxLines> lines of output from the previous command in the pipe and\n"
378 		"silently discards the rest of the output.\n", 0);
379 	add_debugger_command_etc("tail", &cmd_tail,
380 		"Prints only the last lines of output from another command",
381 		"[ <maxLines> ]\n"
382 		"Should be used in a command pipe. It prints only the last\n"
383 		"<maxLines> (default 10) lines of output from the previous command in\n"
384 		"the pipe and silently discards the rest of the output.\n",
385 		B_KDEBUG_PIPE_FINAL_RERUN);
386 	add_debugger_command_etc("grep", &cmd_grep,
387 		"Filters output from another command",
388 		"[ -i ] [ -v ] <pattern>\n"
389 		"Should be used in a command pipe. It filters all output from the\n"
390 		"previous command in the pipe according to the given pattern.\n"
391 		"When \"-v\" is specified, only those lines are printed that don't\n"
392 		"match the given pattern, otherwise only those that do match. When\n"
393 		"\"-i\" is specified, the pattern is matched case insensitive,\n"
394 		"otherwise case sensitive.\n", 0);
395 	add_debugger_command_etc("wc", &cmd_wc,
396 		"Counts the lines, words, and characters of another command's output",
397 		"<maxLines>\n"
398 		"Should be used in a command pipe. It prints how many lines, words,\n"
399 		"and characters the output of the previous command consists of.\n",
400 		B_KDEBUG_PIPE_FINAL_RERUN);
401 }
402