174429Sorion/*	$OpenBSD: db_output.c,v 1.37 2021/06/10 12:33:48 bluhm Exp $	*/
274429Sorion/*	$NetBSD: db_output.c,v 1.13 1996/04/01 17:27:14 christos Exp $	*/
374429Sorion
474429Sorion/*
574429Sorion * Mach Operating System
674429Sorion * Copyright (c) 1993,1992,1991,1990 Carnegie Mellon University
774429Sorion * All Rights Reserved.
874429Sorion *
974429Sorion * Permission to use, copy, modify and distribute this software and its
1074429Sorion * documentation is hereby granted, provided that both the copyright
1174429Sorion * notice and this permission notice appear in all copies of the
1274429Sorion * software, derivative works or modified versions, and any portions
1374429Sorion * thereof, and that both notices appear in supporting documentation.
1474429Sorion *
1574429Sorion * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
1674429Sorion * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
1774429Sorion * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
1874429Sorion *
1974429Sorion * Carnegie Mellon requests users of this software to return to
2074429Sorion *
2174429Sorion *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
2274429Sorion *  School of Computer Science
2374429Sorion *  Carnegie Mellon University
2474429Sorion *  Pittsburgh PA 15213-3890
2574429Sorion *
2674429Sorion * any improvements or extensions that they make and grant Carnegie Mellon
2774429Sorion * the rights to redistribute these changes.
2874429Sorion */
2974429Sorion
3074429Sorion/*
3174429Sorion * Printf and character output for debugger.
3274429Sorion */
3374429Sorion#include <sys/param.h>
3474429Sorion#include <sys/atomic.h>
3574429Sorion#include <sys/stdarg.h>
3674429Sorion#include <sys/systm.h>
3774429Sorion#include <sys/stacktrace.h>
3874429Sorion
3974429Sorion#include <dev/cons.h>
4074429Sorion
4174429Sorion#include <machine/db_machdep.h>
4274429Sorion
4374429Sorion#include <ddb/db_command.h>
4474429Sorion#include <ddb/db_output.h>
4574429Sorion#include <ddb/db_access.h>
4674429Sorion#include <ddb/db_interface.h>
4774429Sorion#include <ddb/db_sym.h>
4874429Sorion#include <ddb/db_var.h>
4974429Sorion
5074429Sorion/*
5174429Sorion *	Character output - tracks position in line.
5274429Sorion *	To do this correctly, we should know how wide
5374429Sorion *	the output device is - then we could zero
5474429Sorion *	the line position when the output device wraps
5574429Sorion *	around to the start of the next line.
5674429Sorion *
5774429Sorion *	Instead, we count the number of spaces printed
5874429Sorion *	since the last printing character so that we
5974429Sorion *	don't print trailing spaces.  This avoids most
6074429Sorion *	of the wraparounds.
6174429Sorion */
6274429Sorion
6374429Sorion#ifndef	DB_MAX_LINE
6474429Sorion#define	DB_MAX_LINE		24	/* maximum line */
6574429Sorion#define DB_MAX_WIDTH		80	/* maximum width */
6674429Sorion#endif	/* DB_MAX_LINE */
6774429Sorion
6874429Sorion#define DB_MIN_MAX_WIDTH	20	/* minimum max width */
6974429Sorion#define DB_MIN_MAX_LINE		3	/* minimum max line */
7074429Sorion#define CTRL(c)			((c) & 0xff)
7174429Sorion
7274429Sorionint	db_output_position = 0;		/* output column */
7374429Sorionint	db_output_line = 0;		/* output line number */
7474429Sorionint	db_last_non_space = 0;		/* last non-space character */
7574429Sorionint	db_tab_stop_width = 8;		/* how wide are tab stops? */
7674429Sorion#define	NEXT_TAB(i) \
7774429Sorion	((((i) + db_tab_stop_width) / db_tab_stop_width) * db_tab_stop_width)
7874429Sorionint	db_max_line = DB_MAX_LINE;	/* output max lines */
7974429Sorionint	db_max_width = DB_MAX_WIDTH;	/* output line width */
8074429Sorionint	db_radix = 16;			/* output numbers radix */
8174429Sorion
8274429Sorionstatic void db_more(void);
8374429Sorion
8474429Sorion/*
8574429Sorion * Force pending whitespace.
8674429Sorion */
8774429Sorionvoid
8874429Soriondb_force_whitespace(void)
8974429Sorion{
9074429Sorion	int last_print, next_tab;
9174429Sorion
9274429Sorion	last_print = db_last_non_space;
9374429Sorion	while (last_print < db_output_position) {
9474429Sorion		next_tab = NEXT_TAB(last_print);
9574429Sorion		if (next_tab <= db_output_position) {
9674429Sorion			while (last_print < next_tab) { /* DON'T send a tab!!! */
9774429Sorion				cnputc(' ');
9874429Sorion				last_print++;
9974429Sorion			}
10074429Sorion		} else {
10174429Sorion			cnputc(' ');
10274429Sorion			last_print++;
10374429Sorion		}
10474429Sorion	}
10574429Sorion	db_last_non_space = db_output_position;
10674429Sorion}
10774429Sorion
10874429Sorionstatic void
10974429Soriondb_more(void)
11074429Sorion{
11174429Sorion	char *p;
11274429Sorion	int quit_output = 0;
11374429Sorion
11474429Sorion	for (p = "--db_more--"; *p; p++)
11574429Sorion		cnputc(*p);
11674429Sorion	switch(cngetc()) {
11774429Sorion	case ' ':
11874429Sorion		db_output_line = 0;
11974429Sorion		break;
12074429Sorion	case 'q':
12174429Sorion	case CTRL('c'):
12274429Sorion		db_output_line = 0;
12374429Sorion		quit_output = 1;
12474429Sorion		break;
12574429Sorion	default:
12674429Sorion		db_output_line--;
12774429Sorion		break;
12874429Sorion	}
12974429Sorion	p = "\b\b\b\b\b\b\b\b\b\b\b           \b\b\b\b\b\b\b\b\b\b\b";
13074429Sorion	while (*p)
13174429Sorion		cnputc(*p++);
13274429Sorion	if (quit_output) {
13374429Sorion		db_error(0);
13474429Sorion		/* NOTREACHED */
13574429Sorion	}
13674429Sorion}
13774429Sorion
13874429Sorion/*
13974429Sorion * Output character.  Buffer whitespace.
14074429Sorion */
14174429Sorionvoid
14274429Soriondb_putchar(int c)
14374429Sorion{
14474429Sorion	if (db_max_line >= DB_MIN_MAX_LINE && db_output_line >= db_max_line-1)
14574429Sorion		db_more();
14674429Sorion
14774429Sorion	if (c > ' ' && c <= '~') {
14874429Sorion		/*
14974429Sorion		 * Printing character.
15074429Sorion		 * If we have spaces to print, print them first.
15174429Sorion		 * Use tabs if possible.
15274429Sorion		 */
15374429Sorion		db_force_whitespace();
15474429Sorion		cnputc(c);
15574429Sorion		db_output_position++;
15674429Sorion		if (db_max_width >= DB_MIN_MAX_WIDTH &&
15774429Sorion		    db_output_position >= db_max_width-1) {
15874429Sorion			/* auto new line */
15974429Sorion			cnputc('\n');
16074429Sorion			db_output_position = 0;
16174429Sorion			db_last_non_space = 0;
16274429Sorion			db_output_line++;
16374429Sorion		}
16474429Sorion		db_last_non_space = db_output_position;
16574429Sorion	} else if (c == '\n') {
16674429Sorion		/* Return */
16774429Sorion		cnputc(c);
16874429Sorion		db_output_position = 0;
16974429Sorion		db_last_non_space = 0;
17074429Sorion		db_output_line++;
17174429Sorion	} else if (c == '\t') {
17274429Sorion		/* assume tabs every 8 positions */
17374429Sorion		db_output_position = NEXT_TAB(db_output_position);
17474429Sorion	} else if (c == ' ') {
17574429Sorion		/* space */
17674429Sorion		db_output_position++;
17774429Sorion	} else if (c == '\007') {
17874429Sorion		/* bell */
17974429Sorion		cnputc(c);
18074429Sorion	}
18174429Sorion	/* other characters are assumed non-printing */
18274429Sorion}
18374429Sorion
18474429Sorion/*
18574429Sorion * Return output position
18674429Sorion */
18774429Sorionint
18874429Soriondb_print_position(void)
18974429Sorion{
19074429Sorion	return (db_output_position);
19174429Sorion}
19274429Sorion
19374429Sorion/*
19474429Sorion * End line if too long.
19574429Sorion */
19674429Sorionvoid
19774429Soriondb_end_line(int space)
19874429Sorion{
19974429Sorion	if (db_output_position >= db_max_width - space)
20074429Sorion		db_printf("\n");
20174429Sorion}
20274429Sorion
20374429Sorionchar *
20474429Soriondb_format(char *buf, size_t bufsize, long val, int format, int alt, int width)
20574429Sorion{
20674429Sorion	const char *fmt;
20774429Sorion
20874429Sorion	if (format == DB_FORMAT_Z || db_radix == 16)
20974429Sorion		fmt = alt ? "-%#*lx" : "-%*lx";
21074429Sorion	else if (db_radix == 8)
21174429Sorion		fmt = alt ? "-%#*lo" : "-%*lo";
21274429Sorion	else
21374429Sorion		fmt = alt ? "-%#*lu" : "-%*lu";
21474429Sorion
21574429Sorion	/* The leading '-' is a nasty (and beautiful) idea from NetBSD */
21674429Sorion	if (val < 0 && format != DB_FORMAT_N)
21774429Sorion		val = -val;
21874429Sorion	else
21974429Sorion		fmt++;
22074429Sorion
22174429Sorion	snprintf(buf, bufsize, fmt, width, val);
22274429Sorion	return (buf);
22374429Sorion}
22474429Sorion
22574429Sorionvoid
22674429Soriondb_stack_dump(void)
22774429Sorion{
22874429Sorion	static struct cpu_info *intrace = NULL;
22974429Sorion	struct cpu_info *tracing, *ci = curcpu();
23074429Sorion
23174429Sorion	tracing = atomic_cas_ptr(&intrace, NULL, ci);
23274429Sorion	if (tracing != NULL) {
23374429Sorion		if (tracing == ci)
23474429Sorion			printf("Faulted in traceback, aborting...\n");
23574429Sorion		else
23674429Sorion			printf("Parallel traceback, suppressed...\n");
23774429Sorion		return;
23874429Sorion	}
23974429Sorion
24074429Sorion	printf("Starting stack trace...\n");
24174429Sorion	db_stack_trace_print((db_expr_t)__builtin_frame_address(0), 1,
24274429Sorion	    256 /* low limit */, "", printf);
24374429Sorion	printf("End of stack trace.\n");
24474429Sorion	membar_producer();
24574429Sorion	intrace = NULL;
24674429Sorion}
24774429Sorion
24874429Sorionvoid
24974429Sorionstacktrace_print(struct stacktrace *st, int (*pr)(const char *, ...))
25074429Sorion{
25174429Sorion	unsigned int i;
25274429Sorion
25374429Sorion	for (i = 0; i < st->st_count; i++) {
25474429Sorion		(*pr)("#%-2u ", i);
25574429Sorion		db_printsym(st->st_pc[i], DB_STGY_PROC, pr);
25674429Sorion		(*pr)("\n");
25774429Sorion	}
25874429Sorion	if (st->st_count == 0)
25974429Sorion		(*pr)("<empty stack trace>\n");
26074429Sorion}
26174429Sorion
26274429Sorionvoid
26374429Soriondb_resize(int cols, int rows)
26474429Sorion{
26574429Sorion	db_max_width = cols;
26674429Sorion	db_max_line = rows;
26774429Sorion}
26874429Sorion