crash/tools.c

7014 lines
173 KiB
C

/* tools.c - core analysis suite
*
* Copyright (C) 1999, 2000, 2001, 2002 Mission Critical Linux, Inc.
* Copyright (C) 2002-2019 David Anderson
* Copyright (C) 2002-2019 Red Hat, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "defs.h"
#include <ctype.h>
#ifdef VALGRIND
#include <valgrind/valgrind.h>
#include <valgrind/memcheck.h>
#endif
static void print_number(struct number_option *, int, int);
static long alloc_hq_entry(void);
struct hq_entry;
static void dealloc_hq_entry(struct hq_entry *);
static void show_options(void);
static void dump_struct_members(struct list_data *, int, ulong);
static void rbtree_iteration(ulong, struct tree_data *, char *);
void dump_struct_members_for_tree(struct tree_data *, int, ulong);
struct req_entry {
char *arg, *name, **member;
int *is_str, *is_ptr;
ulong *width, *offset;
int count;
};
static void print_value(struct req_entry *, unsigned int, ulong, unsigned int);
struct req_entry *fill_member_offsets(char *);
void dump_struct_members_fast(struct req_entry *, int, ulong);
FILE *
set_error(char *target)
{
FILE *tmp_fp = NULL;
char *tmp_str = NULL;
if (STREQ(target, pc->error_path))
return pc->error_fp;
tmp_str = malloc(strlen(target) + 1);
if (tmp_str == NULL)
return NULL;
strcpy(tmp_str, target);
if (STREQ(target, "default"))
tmp_fp = stdout;
else if (STREQ(target, "redirect"))
tmp_fp = fp;
else {
tmp_fp = fopen(target, "a");
if (tmp_fp == NULL) {
error(INFO, "invalid path: %s\n", target);
return NULL;
}
}
if (pc->error_fp != NULL && pc->error_fp != stdout && pc->error_fp != fp)
fclose(pc->error_fp);
if (pc->error_path)
free(pc->error_path);
pc->error_fp = tmp_fp;
pc->error_path = tmp_str;
return pc->error_fp;
}
/*
* General purpose error reporting routine. Type INFO prints the message
* and returns. Type FATAL aborts the command in progress, and longjmps
* back to the appropriate recovery location. If a FATAL occurs during
* program initialization, exit() is called.
*
* The idea is to get the message out so that it is seen by the user
* regardless of how the command output may be piped or redirected.
* Besides stderr, check whether the output is going to a file or pipe, and
* if so, intermingle the error message there as well.
*/
int
__error(int type, char *fmt, ...)
{
int end_of_line, new_line;
char buf[BUFSIZE];
char *spacebuf;
void *retaddr[NUMBER_STACKFRAMES] = { 0 };
va_list ap;
if (STREQ(pc->error_path, "redirect"))
pc->error_fp = fp;
if (CRASHDEBUG(1) || (pc->flags & DROP_CORE)) {
SAVE_RETURN_ADDRESS(retaddr);
console("error() trace: %lx => %lx => %lx => %lx\n",
retaddr[3], retaddr[2], retaddr[1], retaddr[0]);
}
va_start(ap, fmt);
(void)vsnprintf(buf, BUFSIZE, fmt, ap);
va_end(ap);
if (!fmt && FATAL_ERROR(type)) {
fprintf(pc->error_fp, "\n");
clean_exit(1);
}
end_of_line = FATAL_ERROR(type) && !(pc->flags & RUNTIME);
if ((new_line = (buf[0] == '\n')))
shift_string_left(buf, 1);
else if (pc->flags & PLEASE_WAIT)
new_line = TRUE;
if (type == CONT)
spacebuf = space(strlen(pc->curcmd));
else
spacebuf = NULL;
if (pc->stdpipe &&
(STREQ(pc->error_path, "default") || STREQ(pc->error_path, "redirect"))) {
fprintf(pc->stdpipe, "%s%s%s %s%s",
new_line ? "\n" : "",
type == CONT ? spacebuf : pc->curcmd,
type == CONT ? " " : ":",
type == WARNING ? "WARNING: " :
type == NOTE ? "NOTE: " : "",
buf);
fflush(pc->stdpipe);
} else {
fprintf(pc->error_fp, "%s%s%s %s%s",
new_line || end_of_line ? "\n" : "",
type == WARNING ? "WARNING" :
type == NOTE ? "NOTE" :
type == CONT ? spacebuf : pc->curcmd,
type == CONT ? " " : ":",
buf, end_of_line ? "\n" : "");
fflush(pc->error_fp);
}
if ((STREQ(pc->error_path, "default")) &&
(fp != stdout) && (fp != pc->stdpipe) && (fp != pc->tmpfile)) {
fprintf(fp, "%s%s%s %s", new_line ? "\n" : "",
type == WARNING ? "WARNING" :
type == NOTE ? "NOTE" :
type == CONT ? spacebuf : pc->curcmd,
type == CONT ? " " : ":",
buf);
fflush(fp);
}
if ((pc->flags & DROP_CORE) && (type != NOTE)) {
dump_trace(retaddr);
SIGACTION(SIGSEGV, SIG_DFL, &pc->sigaction, NULL);
drop_core("DROP_CORE flag set: forcing a segmentation fault\n");
}
switch (type)
{
case FATAL:
if (pc->flags & IN_FOREACH)
RESUME_FOREACH();
/* FALLTHROUGH */
case FATAL_RESTART:
if (pc->flags & RUNTIME)
RESTART();
else {
if (REMOTE())
remote_exit();
clean_exit(1);
}
default:
case INFO:
case NOTE:
case WARNING:
return FALSE;
}
}
/*
* Parse a line into tokens, populate the passed-in argv[] array, and return
* the count of arguments found. This function modifies the passed-string
* by inserting a NULL character at the end of each token. Expressions
* encompassed by parentheses, and strings encompassed by apostrophes, are
* collected into single tokens.
*/
int
parse_line(char *str, char *argv[])
{
int i, j, k;
int string;
int expression;
for (i = 0; i < MAXARGS; i++)
argv[i] = NULL;
clean_line(str);
if (str == NULL || strlen(str) == 0)
return(0);
i = j = k = 0;
string = FALSE;
expression = 0;
/*
* Special handling for when the first character is a '"'.
*/
if (str[0] == '"') {
next:
do {
i++;
} while ((str[i] != NULLCHAR) && (str[i] != '"'));
switch (str[i])
{
case NULLCHAR:
argv[j] = &str[k];
return j+1;
case '"':
argv[j++] = &str[k+1];
str[i++] = NULLCHAR;
if (str[i] == '"') {
k = i;
goto next;
}
break;
}
} else
argv[j++] = str;
while (TRUE) {
if (j == MAXARGS)
error(FATAL, "too many arguments in string!\n");
while (str[i] != ' ' && str[i] != '\t' && str[i] != NULLCHAR) {
i++;
}
switch (str[i])
{
case ' ':
case '\t':
str[i++] = NULLCHAR;
while (str[i] == ' ' || str[i] == '\t') {
i++;
}
if (str[i] == '"') {
str[i] = ' ';
string = TRUE;
i++;
}
/*
* Make an expression encompassed by a set of parentheses
* a single argument. Also account for embedded sets.
*/
if (!string && str[i] == '(') {
argv[j++] = &str[i];
expression = 1;
while (expression > 0) {
i++;
switch (str[i])
{
case '(':
expression++;
break;
case ')':
expression--;
break;
case NULLCHAR:
case '\n':
expression = -1;
break;
default:
break;
}
}
if (expression == 0) {
i++;
continue;
}
}
if (str[i] != NULLCHAR && str[i] != '\n') {
argv[j++] = &str[i];
if (string) {
string = FALSE;
while (str[i] != '"' && str[i] != NULLCHAR)
i++;
if (str[i] == '"')
str[i] = ' ';
}
break;
}
/* else fall through */
case '\n':
str[i] = NULLCHAR;
/* keep falling... */
case NULLCHAR:
argv[j] = NULLCHAR;
return(j);
}
}
}
/*
* Defuse controversy re: extensions to ctype.h
*/
int
whitespace(int c)
{
return ((c == ' ') ||(c == '\t'));
}
int
ascii(int c)
{
return ((c >= 0) && ( c <= 0x7f));
}
/*
* Strip line-ending whitespace and linefeeds.
*/
char *
strip_line_end(char *line)
{
strip_linefeeds(line);
strip_ending_whitespace(line);
return(line);
}
/*
* Strip line-beginning and line-ending whitespace and linefeeds.
*/
char *
clean_line(char *line)
{
strip_beginning_whitespace(line);
strip_linefeeds(line);
strip_ending_whitespace(line);
return(line);
}
/*
* Strip line-ending linefeeds in a string.
*/
char *
strip_linefeeds(char *line)
{
char *p;
if (line == NULL || strlen(line) == 0)
return(line);
p = &LASTCHAR(line);
while (*p == '\n') {
*p = NULLCHAR;
if (--p < line)
break;
}
return(line);
}
/*
* Strip a specified line-ending character in a string.
*/
char *
strip_ending_char(char *line, char c)
{
char *p;
if (line == NULL || strlen(line) == 0)
return(line);
p = &LASTCHAR(line);
if (*p == c)
*p = NULLCHAR;
return(line);
}
/*
* Strip a specified line-beginning character in a string.
*/
char *
strip_beginning_char(char *line, char c)
{
if (line == NULL || strlen(line) == 0)
return(line);
if (FIRSTCHAR(line) == c)
shift_string_left(line, 1);
return(line);
}
/*
* Strip line-ending whitespace.
*/
char *
strip_ending_whitespace(char *line)
{
char *p;
if (line == NULL || strlen(line) == 0)
return(line);
p = &LASTCHAR(line);
while (*p == ' ' || *p == '\t') {
*p = NULLCHAR;
if (p == line)
break;
p--;
}
return(line);
}
/*
* Strip line-beginning whitespace.
*/
char *
strip_beginning_whitespace(char *line)
{
char buf[BUFSIZE];
char *p;
if (line == NULL || strlen(line) == 0)
return(line);
strcpy(buf, line);
p = &buf[0];
while (*p == ' ' || *p == '\t')
p++;
strcpy(line, p);
return(line);
}
/*
* End line at first comma found.
*/
char *
strip_comma(char *line)
{
char *p;
if ((p = strstr(line, ",")))
*p = NULLCHAR;
return(line);
}
/*
* Strip the 0x from the beginning of a hexadecimal value string.
*/
char *
strip_hex(char *line)
{
if (STRNEQ(line, "0x"))
shift_string_left(line, 2);
return(line);
}
/*
* Turn a string into upper-case.
*/
char *
upper_case(const char *s, char *buf)
{
const char *p1;
char *p2;
p1 = s;
p2 = buf;
while (*p1) {
*p2 = toupper(*p1);
p1++, p2++;
}
*p2 = NULLCHAR;
return(buf);
}
/*
* Return pointer to first non-space/tab in a string.
*/
char *
first_nonspace(char *s)
{
return(s + strspn(s, " \t"));
}
/*
* Return pointer to first space/tab in a string. If none are found,
* return a pointer to the string terminating NULL.
*/
char *
first_space(char *s)
{
return(s + strcspn(s, " \t"));
}
/*
* Replace the first space/tab found in a string with a NULL character.
*/
char *
null_first_space(char *s)
{
char *p1;
p1 = first_space(s);
if (*p1)
*p1 = NULLCHAR;
return s;
}
/*
* Replace any instances of the characters in string c that are found in
* string s with the character passed in r.
*/
char *
replace_string(char *s, char *c, char r)
{
int i, j;
for (i = 0; s[i]; i++) {
for (j = 0; c[j]; j++) {
if (s[i] == c[j])
s[i] = r;
}
}
return s;
}
void
string_insert(char *insert, char *where)
{
char *p;
p = GETBUF(strlen(insert) + strlen(where) + 1);
sprintf(p, "%s%s", insert, where);
strcpy(where, p);
FREEBUF(p);
}
/*
* Find the rightmost instance of a substring in a string.
*/
char *
strstr_rightmost(char *s, char *lookfor)
{
char *next, *last, *p;
for (p = s, last = NULL; *p; p++) {
if (!(next = strstr(p, lookfor)))
break;
last = p = next;
}
return last;
}
/*
* Prints a string verbatim, allowing strings with % signs to be displayed
* without printf conversions.
*/
void
print_verbatim(FILE *filep, char *line)
{
int i;
for (i = 0; i < strlen(line); i++) {
fputc(line[i], filep);
fflush(filep);
}
}
char *
fixup_percent(char *s)
{
char *p1;
if ((p1 = strstr(s, "%")) == NULL)
return s;
s[strlen(s)+1] = NULLCHAR;
memmove(p1+1, p1, strlen(p1));
*p1 = '%';
return s;
}
/*
* Convert an indeterminate number string to either a hexadecimal or decimal
* long value. Translate with a bias towards decimal unless HEX_BIAS is set.
*/
ulong
stol(char *s, int flags, int *errptr)
{
if ((flags & HEX_BIAS) && hexadecimal(s, 0))
return(htol(s, flags, errptr));
else {
if (decimal(s, 0))
return(dtol(s, flags, errptr));
else if (hexadecimal(s, 0))
return(htol(s, flags, errptr));
}
if (!(flags & QUIET))
error(INFO, "not a valid number: %s\n", s);
switch (flags & (FAULT_ON_ERROR|RETURN_ON_ERROR))
{
case FAULT_ON_ERROR:
RESTART();
case RETURN_ON_ERROR:
if (errptr)
*errptr = TRUE;
break;
}
return UNUSED;
}
ulonglong
stoll(char *s, int flags, int *errptr)
{
if ((flags & HEX_BIAS) && hexadecimal(s, 0))
return(htoll(s, flags, errptr));
else {
if (decimal(s, 0))
return(dtoll(s, flags, errptr));
else if (hexadecimal(s, 0))
return(htoll(s, flags, errptr));
}
if (!(flags & QUIET))
error(INFO, "not a valid number: %s\n", s);
switch (flags & (FAULT_ON_ERROR|RETURN_ON_ERROR))
{
case FAULT_ON_ERROR:
RESTART();
case RETURN_ON_ERROR:
if (errptr)
*errptr = TRUE;
break;
}
return UNUSED;
}
/*
* Append a two-character string to a number to make 1, 2, 3 and 4 into
* 1st, 2nd, 3rd, 4th, and so on...
*/
char *
ordinal(ulong val, char *buf)
{
char *p1;
sprintf(buf, "%ld", val);
p1 = &buf[strlen(buf)-1];
switch (*p1)
{
case '1':
strcat(buf, "st");
break;
case '2':
strcat(buf, "nd");
break;
case '3':
strcat(buf, "rd");
break;
default:
strcat(buf, "th");
break;
}
return buf;
}
/*
* Convert a string into:
*
* 1. an evaluated expression if it's enclosed within parentheses.
* 2. to a decimal value if the string is all decimal characters.
* 3. to a hexadecimal value if the string is all hexadecimal characters.
* 4. to a symbol value if the string is a known symbol.
*
* If HEX_BIAS is set, pass the value on to htol().
*/
ulong
convert(char *s, int flags, int *errptr, ulong numflag)
{
struct syment *sp;
if ((numflag & NUM_EXPR) && can_eval(s))
return(eval(s, flags, errptr));
if ((flags & HEX_BIAS) && (numflag & NUM_HEX) && hexadecimal(s, 0))
return(htol(s, flags, errptr));
else {
if ((numflag & NUM_DEC) && decimal(s, 0))
return(dtol(s, flags, errptr));
if ((numflag & NUM_HEX) && hexadecimal(s, 0))
return(htol(s, flags, errptr));
}
if ((sp = symbol_search(s)))
return(sp->value);
error(INFO, "cannot convert \"%s\"\n", s);
switch (flags & (FAULT_ON_ERROR|RETURN_ON_ERROR))
{
case FAULT_ON_ERROR:
RESTART();
case RETURN_ON_ERROR:
if (errptr)
*errptr = TRUE;
break;
}
return UNUSED;
}
/*
* Convert a string to a hexadecimal long value.
*/
ulong
htol(char *s, int flags, int *errptr)
{
long i, j;
ulong n;
if (s == NULL) {
if (!(flags & QUIET))
error(INFO, "received NULL string\n");
goto htol_error;
}
if (STRNEQ(s, "0x") || STRNEQ(s, "0X"))
s += 2;
if (strlen(s) > MAX_HEXADDR_STRLEN) {
if (!(flags & QUIET))
error(INFO,
"input string too large: \"%s\" (%d vs %d)\n",
s, strlen(s), MAX_HEXADDR_STRLEN);
goto htol_error;
}
for (n = i = 0; s[i] != 0; i++) {
switch (s[i])
{
case 'a':
case 'b':
case 'c':
case 'd':
case 'e':
case 'f':
j = (s[i] - 'a') + 10;
break;
case 'A':
case 'B':
case 'C':
case 'D':
case 'E':
case 'F':
j = (s[i] - 'A') + 10;
break;
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '0':
j = s[i] - '0';
break;
case 'x':
case 'X':
case 'h':
continue;
default:
if (!(flags & QUIET))
error(INFO, "invalid input: \"%s\"\n", s);
goto htol_error;
}
n = (16 * n) + j;
}
return(n);
htol_error:
switch (flags & (FAULT_ON_ERROR|RETURN_ON_ERROR))
{
case FAULT_ON_ERROR:
RESTART();
case RETURN_ON_ERROR:
if (errptr)
*errptr = TRUE;
break;
}
return BADADDR;
}
/*
* Convert a string to a hexadecimal unsigned long long value.
*/
ulonglong
htoll(char *s, int flags, int *errptr)
{
long i, j;
ulonglong n;
if (s == NULL) {
if (!(flags & QUIET))
error(INFO, "received NULL string\n");
goto htoll_error;
}
if (STRNEQ(s, "0x") || STRNEQ(s, "0X"))
s += 2;
if (strlen(s) > LONG_LONG_PRLEN) {
if (!(flags & QUIET))
error(INFO,
"input string too large: \"%s\" (%d vs %d)\n",
s, strlen(s), LONG_LONG_PRLEN);
goto htoll_error;
}
for (n = i = 0; s[i] != 0; i++) {
switch (s[i])
{
case 'a':
case 'b':
case 'c':
case 'd':
case 'e':
case 'f':
j = (s[i] - 'a') + 10;
break;
case 'A':
case 'B':
case 'C':
case 'D':
case 'E':
case 'F':
j = (s[i] - 'A') + 10;
break;
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '0':
j = s[i] - '0';
break;
case 'x':
case 'X':
case 'h':
continue;
default:
if (!(flags & QUIET))
error(INFO, "invalid input: \"%s\"\n", s);
goto htoll_error;
}
n = (16 * n) + j;
}
return(n);
htoll_error:
switch (flags & (FAULT_ON_ERROR|RETURN_ON_ERROR))
{
case FAULT_ON_ERROR:
RESTART();
case RETURN_ON_ERROR:
if (errptr)
*errptr = TRUE;
break;
}
return UNUSED;
}
/*
* Convert a string to a decimal long value.
*/
ulong
dtol(char *s, int flags, int *errptr)
{
ulong retval;
char *p, *orig;
int j;
if (s == NULL) {
if (!(flags & QUIET))
error(INFO, "received NULL string\n");
goto dtol_error;
}
if (strlen(s) == 0)
goto dtol_error;
p = orig = &s[0];
while (*p++ == ' ')
s++;
for (j = 0; s[j] != '\0'; j++)
if ((s[j] < '0' || s[j] > '9'))
break ;
if (s[j] != '\0') {
if (!(flags & QUIET))
error(INFO, "%s: \"%c\" is not a digit 0 - 9\n",
orig, s[j]);
goto dtol_error;
} else if (sscanf(s, "%lu", &retval) != 1) {
if (!(flags & QUIET))
error(INFO, "invalid expression\n");
goto dtol_error;
}
return(retval);
dtol_error:
switch (flags & (FAULT_ON_ERROR|RETURN_ON_ERROR))
{
case FAULT_ON_ERROR:
RESTART();
case RETURN_ON_ERROR:
if (errptr)
*errptr = TRUE;
break;
}
return UNUSED;
}
/*
* Convert a string to a decimal long value.
*/
ulonglong
dtoll(char *s, int flags, int *errptr)
{
ulonglong retval;
char *p, *orig;
int j;
if (s == NULL) {
if (!(flags & QUIET))
error(INFO, "received NULL string\n");
goto dtoll_error;
}
if (strlen(s) == 0)
goto dtoll_error;
p = orig = &s[0];
while (*p++ == ' ')
s++;
for (j = 0; s[j] != '\0'; j++)
if ((s[j] < '0' || s[j] > '9'))
break ;
if (s[j] != '\0') {
if (!(flags & QUIET))
error(INFO, "%s: \"%c\" is not a digit 0 - 9\n",
orig, s[j]);
goto dtoll_error;
} else if (sscanf(s, "%llu", &retval) != 1) {
if (!(flags & QUIET))
error(INFO, "invalid expression\n");
goto dtoll_error;
}
return (retval);
dtoll_error:
switch (flags & (FAULT_ON_ERROR|RETURN_ON_ERROR))
{
case FAULT_ON_ERROR:
RESTART();
case RETURN_ON_ERROR:
if (errptr)
*errptr = TRUE;
break;
}
return ((ulonglong)(-1));
}
/*
* Convert a string to a decimal integer value.
*/
unsigned int
dtoi(char *s, int flags, int *errptr)
{
unsigned int retval;
char *p;
int j;
if (s == NULL) {
if (!(flags & QUIET))
error(INFO, "received NULL string\n");
goto dtoi_error;
}
p = &s[0];
while (*p++ == ' ')
s++;
for (j = 0; s[j] != '\0'; j++)
if ((s[j] < '0' || s[j] > '9'))
break ;
if (s[j] != '\0' || (sscanf(s, "%d", (int *)&retval) != 1)) {
if (!(flags & QUIET))
error(INFO, "%s: \"%c\" is not a digit 0 - 9\n",
s, s[j]);
goto dtoi_error;
}
return(retval);
dtoi_error:
switch (flags & (FAULT_ON_ERROR|RETURN_ON_ERROR))
{
case FAULT_ON_ERROR:
RESTART();
case RETURN_ON_ERROR:
if (errptr)
*errptr = TRUE;
break;
}
return((unsigned int)(-1));
}
/*
* Determine whether a string contains only decimal characters.
* If count is non-zero, limit the search to count characters.
*/
int
decimal(char *s, int count)
{
char *p;
int cnt, digits;
if (!count) {
strip_line_end(s);
cnt = 0;
} else
cnt = count;
for (p = &s[0], digits = 0; *p; p++) {
switch(*p)
{
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
digits++;
case ' ':
break;
default:
return FALSE;
}
if (count && (--cnt == 0))
break;
}
return (digits ? TRUE : FALSE);
}
/*
* Extract a hexadecimal number from a string. If first_instance is FALSE,
* and two possibilities are found, a fatal error results.
*/
int
extract_hex(char *s, ulong *result, char stripchar, ulong first_instance)
{
int i, found;
char *arglist[MAXARGS];
int argc;
ulong value;
char *buf;
buf = GETBUF(strlen(s) + 1);
strcpy(buf, s);
argc = parse_line(buf, arglist);
for (i = found = value = 0; i < argc; i++) {
if (stripchar)
strip_ending_char(arglist[i], stripchar);
if (hexadecimal(arglist[i], 0)) {
if (found) {
FREEBUF(buf);
error(FATAL,
"two hexadecimal args in: \"%s\"\n",
strip_linefeeds(s));
}
value = htol(arglist[i], FAULT_ON_ERROR, NULL);
found = TRUE;
if (first_instance)
break;
}
}
FREEBUF(buf);
if (found) {
*result = value;
return TRUE;
}
return FALSE;
}
/*
* Determine whether a string contains only ASCII characters.
*/
int
ascii_string(char *s)
{
char *p;
for (p = &s[0]; *p; p++) {
if (!ascii(*p))
return FALSE;
}
return TRUE;
}
/*
* Check whether a string contains only printable ASCII characters.
*/
int
printable_string(char *s)
{
char *p;
for (p = &s[0]; *p; p++) {
if (!isprint(*p))
return FALSE;
}
return TRUE;
}
/*
* Determine whether a string contains only hexadecimal characters.
* If count is non-zero, limit the search to count characters.
*/
int
hexadecimal(char *s, int count)
{
char *p;
int cnt, digits;
if (!count) {
strip_line_end(s);
cnt = 0;
} else
cnt = count;
for (p = &s[0], digits = 0; *p; p++) {
switch(*p)
{
case 'a':
case 'b':
case 'c':
case 'd':
case 'e':
case 'f':
case 'A':
case 'B':
case 'C':
case 'D':
case 'E':
case 'F':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '0':
digits++;
case 'x':
case 'X':
break;
case ' ':
if (*(p+1) == NULLCHAR)
break;
else
return FALSE;
default:
return FALSE;
}
if (count && (--cnt == 0))
break;
}
return (digits ? TRUE : FALSE);
}
/*
* Determine whether a string contains only hexadecimal characters.
* and cannot be construed as a decimal number.
* If count is non-zero, limit the search to count characters.
*/
int
hexadecimal_only(char *s, int count)
{
char *p;
int cnt, only;
if (!count) {
strip_line_end(s);
cnt = 0;
} else
cnt = count;
only = 0;
for (p = &s[0]; *p; p++) {
switch(*p)
{
case 'a':
case 'b':
case 'c':
case 'd':
case 'e':
case 'f':
case 'A':
case 'B':
case 'C':
case 'D':
case 'E':
case 'F':
case 'x':
case 'X':
only++;
break;
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '0':
break;
case ' ':
if (*(p+1) == NULLCHAR)
break;
else
return FALSE;
default:
return FALSE;
}
if (count && (--cnt == 0))
break;
}
return only;
}
/*
* Clean a command argument that has an obvious but ignorable error.
* The first one is an attached comma to a number, that usually is the
* result of a cut-and-paste of an address from a structure display.
* The second on is an attached colon to a number, usually from a
* cut-and-paste of a memory dump.
* Add more when they become annoynance.
*
* It presumes args[optind] is the argument being tinkered with, and
* always returns TRUE for convenience of use.
*/
int
clean_arg(void)
{
char buf[BUFSIZE];
if (LASTCHAR(args[optind]) == ',' ||
LASTCHAR(args[optind]) == ':') {
strcpy(buf, args[optind]);
LASTCHAR(buf) = NULLCHAR;
if (IS_A_NUMBER(buf))
LASTCHAR(args[optind]) = NULLCHAR;
}
return TRUE;
}
/*
* Translate a hexadecimal string into its ASCII components.
*/
void
cmd_ascii(void)
{
int i;
ulonglong value;
char *s;
int c, prlen, bytes;
optind = 1;
if (!args[optind]) {
fprintf(fp, "\n");
fprintf(fp, " 0 1 2 3 4 5 6 7\n");
fprintf(fp, " +-------------------------------\n");
fprintf(fp, " 0 | NUL DLE SP 0 @ P ' p\n");
fprintf(fp, " 1 | SOH DC1 ! 1 A Q a q\n");
fprintf(fp, " 2 | STX DC2 %c 2 B R b r\n", 0x22);
fprintf(fp, " 3 | ETX DC3 # 3 C S c s\n");
fprintf(fp, " 4 | EOT DC4 $ 4 D T d t\n");
fprintf(fp, " 5 | ENQ NAK %c 5 E U e u\n", 0x25);
fprintf(fp, " 6 | ACK SYN & 6 F V f v\n");
fprintf(fp, " 7 | BEL ETB ` 7 G W g w\n");
fprintf(fp, " 8 | BS CAN ( 8 H X h x\n");
fprintf(fp, " 9 | HT EM ) 9 I Y i y\n");
fprintf(fp, " A | LF SUB * : J Z j z\n");
fprintf(fp, " B | VT ESC + ; K [ k {\n");
fprintf(fp, " C | FF FS , < L %c l |\n", 0x5c);
fprintf(fp, " D | CR GS _ = M ] m }\n");
fprintf(fp, " E | SO RS . > N ^ n ~\n");
fprintf(fp, " F | SI US / ? O - o DEL\n");
fprintf(fp, "\n");
return;
}
while (args[optind]) {
s = args[optind];
if (STRNEQ(s, "0x") || STRNEQ(s, "0X"))
s += 2;
if (strlen(s) > LONG_PRLEN) {
prlen = LONG_LONG_PRLEN;
bytes = sizeof(long long);
} else {
prlen = LONG_PRLEN;
bytes = sizeof(long);
}
value = htoll(s, FAULT_ON_ERROR, NULL);
fprintf(fp, "%.*llx: ", prlen, value);
for (i = 0; i < bytes; i++) {
c = (value >> (8*i)) & 0xff;
if ((c >= 0x20) && (c < 0x7f)) {
fprintf(fp, "%c", (char)c);
continue;
}
if (c > 0x7f) {
fprintf(fp, "<%02x>", c);
continue;
}
switch (c)
{
case 0x0: fprintf(fp, "<NUL>"); break;
case 0x1: fprintf(fp, "<SOH>"); break;
case 0x2: fprintf(fp, "<STX>"); break;
case 0x3: fprintf(fp, "<ETX>"); break;
case 0x4: fprintf(fp, "<EOT>"); break;
case 0x5: fprintf(fp, "<ENQ>"); break;
case 0x6: fprintf(fp, "<ACK>"); break;
case 0x7: fprintf(fp, "<BEL>"); break;
case 0x8: fprintf(fp, "<BS>"); break;
case 0x9: fprintf(fp, "<HT>"); break;
case 0xa: fprintf(fp, "<LF>"); break;
case 0xb: fprintf(fp, "<VT>"); break;
case 0xc: fprintf(fp, "<FF>"); break;
case 0xd: fprintf(fp, "<CR>"); break;
case 0xe: fprintf(fp, "<SO>"); break;
case 0xf: fprintf(fp, "<SI>"); break;
case 0x10: fprintf(fp, "<DLE>"); break;
case 0x11: fprintf(fp, "<DC1>"); break;
case 0x12: fprintf(fp, "<DC2>"); break;
case 0x13: fprintf(fp, "<DC3>"); break;
case 0x14: fprintf(fp, "<DC4>"); break;
case 0x15: fprintf(fp, "<NAK>"); break;
case 0x16: fprintf(fp, "<SYN>"); break;
case 0x17: fprintf(fp, "<ETB>"); break;
case 0x18: fprintf(fp, "<CAN>"); break;
case 0x19: fprintf(fp, "<EM>"); break;
case 0x1a: fprintf(fp, "<SUB>"); break;
case 0x1b: fprintf(fp, "<ESC>"); break;
case 0x1c: fprintf(fp, "<FS>"); break;
case 0x1d: fprintf(fp, "<GS>"); break;
case 0x1e: fprintf(fp, "<RS>"); break;
case 0x1f: fprintf(fp, "<US>"); break;
case 0x7f: fprintf(fp, "<DEL>"); break;
}
}
fprintf(fp, "\n");
optind++;
}
}
/*
* Counts number of leading whitespace characters in a string.
*/
int
count_leading_spaces(char *s)
{
return (strspn(s, " \t"));
}
/*
* Prints the requested number of spaces.
*/
void
pad_line(FILE *filep, int cnt, char c)
{
int i;
for (i = 0; i < cnt; i++)
fputc(c, filep);
}
/*
* Returns appropriate number of inter-field spaces in a usable string.
* MINSPACE is defined as -100, but implies the minimum space between two
* fields. Currently this can be either one or two spaces, depending upon
* the architecture. Since the mininum space must be at least 1, MINSPACE,
* MINSPACE-1 and MINSPACE+1 are all valid, special numbers. Otherwise
* the space count must be greater than or equal to 0.
*
* If the cnt request is greater than SPACES, a dynamic buffer is
* allocated, and normal buffer garbage collection will return it
* back to the pool.
*/
char *
space(int cnt)
{
#define SPACES 40
static char spacebuf[SPACES+1] = { 0 };
int i;
char *bigspace;
if (cnt > SPACES) {
bigspace = GETBUF(cnt);
for (i = 0; i < cnt; i++)
bigspace[i] = ' ';
bigspace[i] = NULLCHAR;
return bigspace;
}
if (!strlen(spacebuf)) {
for (i = 0; i < SPACES; i++)
spacebuf[i] = ' ';
spacebuf[i] = NULLCHAR;
}
if (cnt < (MINSPACE-1))
error(FATAL, "illegal spacing request: %d\n", cnt);
if ((cnt > MINSPACE+1) && (cnt < 0))
error(FATAL, "illegal spacing request\n");
switch (cnt)
{
case (MINSPACE-1):
if (VADDR_PRLEN > 8)
return (&spacebuf[SPACES]); /* NULL */
else
return (&spacebuf[SPACES-1]); /* 1 space */
case MINSPACE:
if (VADDR_PRLEN > 8)
return (&spacebuf[SPACES-1]); /* 1 space */
else
return (&spacebuf[SPACES-2]); /* 2 spaces */
case (MINSPACE+1):
if (VADDR_PRLEN > 8)
return (&spacebuf[SPACES-2]); /* 2 spaces */
else
return (&spacebuf[SPACES-3]); /* 3 spaces */
default:
return (&spacebuf[SPACES-cnt]); /* as requested */
}
}
/*
* Determine whether substring s1, with length len, and contained within
* string s, is surrounded by <bracket> characters. If len is 0, calculate
* it.
*/
int
bracketed(char *s, char *s1, int len)
{
char *s2;
if (!len) {
if (!(s2 = strstr(s1, ">")))
return FALSE;
len = s2-s1;
}
if (((s1-s) < 1) || (*(s1-1) != '<') ||
((s1+len) >= &s[strlen(s)]) || (*(s1+len) != '>'))
return FALSE;
return TRUE;
}
/*
* Counts the number of a specified character in a string.
*/
int
count_chars(char *s, char c)
{
char *p;
int count;
if (!s)
return 0;
count = 0;
for (p = s; *p; p++) {
if (*p == c)
count++;
}
return count;
}
/*
* Counts the number of a specified characters in a buffer.
*/
long count_buffer_chars(char *bufptr, char c, long len)
{
long i, cnt;
for (i = cnt = 0; i < len; i++, bufptr++) {
if (*bufptr == c)
cnt++;
}
return cnt;
}
/*
* Concatenates the tokens in the global args[] array into one string,
* separating each token with one space. If the no_options flag is set,
* don't include any args beginning with a dash character.
*/
char *
concat_args(char *buf, int arg, int no_options)
{
int i;
BZERO(buf, BUFSIZE);
for (i = arg; i < argcnt; i++) {
if (no_options && STRNEQ(args[i], "-"))
continue;
strcat(buf, args[i]);
strcat(buf, " ");
}
return(strip_ending_whitespace(buf));
}
/*
* Shifts the contents of a string to the left by cnt characters,
* disposing the leftmost characters.
*/
char *
shift_string_left(char *s, int cnt)
{
int origlen;
if (!cnt)
return(s);
origlen = strlen(s);
memmove(s, s+cnt, (origlen-cnt));
*(s+(origlen-cnt)) = NULLCHAR;
return(s);
}
/*
* Shifts the contents of a string to the right by cnt characters,
* inserting space characters. (caller confirms space is available)
*/
char *
shift_string_right(char *s, int cnt)
{
int origlen;
if (!cnt)
return(s);
origlen = strlen(s);
memmove(s+cnt, s, origlen);
s[origlen+cnt] = NULLCHAR;
return(memset(s, ' ', cnt));
}
/*
* Create a string in a buffer of a given size, centering, or justifying
* left or right as requested. If the opt argument is used, then the string
* is created with its string/integer value. If opt is NULL, then the
* string is already in contained in string s (not justified). Note that
* flag LONGLONG_HEX implies that opt is a ulonglong pointer to the
* actual value.
*/
char *
mkstring(char *s, int size, ulong flags, const char *opt)
{
int len;
int extra;
int left;
int right;
switch (flags & (LONG_DEC|SLONG_DEC|LONG_HEX|INT_HEX|INT_DEC|LONGLONG_HEX|ZERO_FILL))
{
case LONG_DEC:
sprintf(s, "%lu", (ulong)opt);
break;
case SLONG_DEC:
sprintf(s, "%ld", (ulong)opt);
break;
case LONG_HEX:
sprintf(s, "%lx", (ulong)opt);
break;
case (LONG_HEX|ZERO_FILL):
if (VADDR_PRLEN == 8)
sprintf(s, "%08lx", (ulong)opt);
else if (VADDR_PRLEN == 16)
sprintf(s, "%016lx", (ulong)opt);
break;
case INT_DEC:
sprintf(s, "%u", (uint)((ulong)opt));
break;
case INT_HEX:
sprintf(s, "%x", (uint)((ulong)opt));
break;
case LONGLONG_HEX:
sprintf(s, "%llx", *((ulonglong *)opt));
break;
default:
if (opt)
strcpy(s, opt);
break;
}
/*
* At this point, string s has the string to be justified,
* and has room to work with. The relevant flags from this
* point on are of CENTER, LJUST and RJUST. If the length
* of string s is already larger than the requested size,
* just return it as is.
*/
len = strlen(s);
if (size <= len)
return(s);
extra = size - len;
if (flags & CENTER) {
/*
* If absolute centering is not possible, justify the
* string as requested -- or to the left if no justify
* argument was passed in.
*/
if (extra % 2) {
switch (flags & (LJUST|RJUST))
{
default:
case LJUST:
right = (extra/2) + 1;
left = extra/2;
break;
case RJUST:
right = extra/2;
left = (extra/2) + 1;
break;
}
}
else
left = right = extra/2;
shift_string_right(s, left);
len = strlen(s);
memset(s + len, ' ', right);
s[len + right] = NULLCHAR;
return(s);
}
if (flags & LJUST) {
len = strlen(s);
memset(s + len, ' ', extra);
s[len + extra] = NULLCHAR;
} else if (flags & RJUST)
shift_string_right(s, extra);
return(s);
}
/*
* Prints the requested number of BACKSPACE characters.
*/
void
backspace(int cnt)
{
int i;
for (i = 0; i < cnt; i++)
fprintf(fp, "\b");
}
/*
* Set/display process context or internal variables. Processes are set
* by their task or PID number, or to the panic context with the -p flag.
* Internal variables may be viewed or changed, depending whether an argument
* follows the variable name. If no arguments are entered, the current
* process context is dumped. The current set of variables and their
* acceptable settings are:
*
* debug "on", "off", or any number. "on" sets it to a value of 1.
* hash "on", "off", or any number. Non-zero numbers are converted
* to "on", zero is converted to "off".
* scroll "on", "off", or any number. Non-zero numbers are converted
* to "on", zero is converted to "off".
* silent "on", "off", or any number. Non-zero numbers are converted
* to "on", zero is converted to "off".
* refresh "on", "off", or any number. Non-zero numbers are converted
* to "on", zero is converted to "off".
* sym regular filename
* console device filename
* radix 10 or 16
* core (no arg) drop core when error() is called.
* vi (no arg) set editing mode to vi (from .rc file only).
* emacs (no arg) set editing mode to emacs (from .rc file only).
* namelist kernel name (from .rc file only).
* dumpfile dumpfile name (from .rc file only).
*
* gdb variable settings not changeable by gdb's "set" command:
*
* print_max value (default is 200).
*/
void
cmd_set(void)
{
int i, c;
ulong value;
int cpu, runtime, from_rc_file;
char buf[BUFSIZE];
char *extra_message;
struct task_context *tc;
struct syment *sp;
#define defer() do { } while (0)
#define already_done() do { } while (0)
#define ignore() do { } while (0)
extra_message = NULL;
runtime = pc->flags & RUNTIME ? TRUE : FALSE;
from_rc_file = pc->curcmd_flags & FROM_RCFILE ? TRUE : FALSE;
while ((c = getopt(argcnt, args, "pvc:a:")) != EOF) {
switch(c)
{
case 'c':
if (XEN_HYPER_MODE() || (pc->flags & MINIMAL_MODE))
option_not_supported(c);
if (!runtime)
return;
if (ACTIVE()) {
error(INFO, "not allowed on a live system\n");
argerrs++;
break;
}
cpu = dtoi(optarg, FAULT_ON_ERROR, NULL);
set_cpu(cpu);
return;
case 'p':
if (XEN_HYPER_MODE() || (pc->flags & MINIMAL_MODE))
option_not_supported(c);
if (!runtime)
return;
if (ACTIVE()) {
set_context(tt->this_task, NO_PID, TRUE);
show_context(CURRENT_CONTEXT());
return;
}
if (!tt->panic_task) {
error(INFO, "no panic task found!\n");
return;
}
set_context(tt->panic_task, NO_PID, TRUE);
show_context(CURRENT_CONTEXT());
return;
case 'v':
if (!runtime)
return;
show_options();
return;
case 'a':
if (XEN_HYPER_MODE() || (pc->flags & MINIMAL_MODE))
option_not_supported(c);
if (!runtime)
return;
if (ACTIVE())
error(FATAL,
"-a option not allowed on live systems\n");
switch (str_to_context(optarg, &value, &tc))
{
case STR_PID:
if ((i = TASKS_PER_PID(value)) > 1)
error(FATAL,
"pid %d has %d tasks: "
"use a task address\n",
value, i);
break;
case STR_TASK:
break;
case STR_INVALID:
error(FATAL, "invalid task or pid value: %s\n",
optarg);
}
cpu = tc->processor;
tt->active_set[cpu] = tc->task;
if (tt->panic_threads[cpu])
tt->panic_threads[cpu] = tc->task;
fprintf(fp,
"\"%s\" task %lx has been marked as the active task on cpu %d\n",
tc->comm, tc->task, cpu);
return;
default:
argerrs++;
break;
}
}
if (argerrs) {
if (runtime)
cmd_usage(pc->curcmd, SYNOPSIS);
return;
}
if (!args[optind]) {
if (XEN_HYPER_MODE())
error(INFO,
"requires an option with the Xen hypervisor\n");
else if (pc->flags & MINIMAL_MODE)
show_options();
else if (runtime)
show_context(CURRENT_CONTEXT());
return;
}
while (args[optind]) {
if (STREQ(args[optind], "debug")) {
if (args[optind+1]) {
optind++;
if (!runtime)
defer();
else if (STREQ(args[optind], "on"))
pc->debug = 1;
else if (STREQ(args[optind], "off"))
pc->debug = 0;
else if (IS_A_NUMBER(args[optind]))
pc->debug = stol(args[optind],
FAULT_ON_ERROR, NULL);
else
goto invalid_set_command;
}
if (runtime)
fprintf(fp, "debug: %ld\n", pc->debug);
set_lkcd_debug(pc->debug);
set_vas_debug(pc->debug);
return;
} else if (STREQ(args[optind], "hash")) {
if (args[optind+1]) {
optind++;
if (!runtime)
defer();
else if (STREQ(args[optind], "on"))
pc->flags |= HASH;
else if (STREQ(args[optind], "off"))
pc->flags &= ~HASH;
else if (IS_A_NUMBER(args[optind])) {
value = stol(args[optind],
FAULT_ON_ERROR, NULL);
if (value)
pc->flags |= HASH;
else
pc->flags &= ~HASH;
} else
goto invalid_set_command;
}
if (runtime)
fprintf(fp, "hash: %s\n",
pc->flags & HASH ? "on" : "off");
return;
} else if (STREQ(args[optind], "unwind")) {
if (args[optind+1]) {
optind++;
if (!runtime)
defer();
else if (STREQ(args[optind], "on")) {
if ((kt->flags & DWARF_UNWIND_CAPABLE) ||
!runtime) {
kt->flags |= DWARF_UNWIND;
kt->flags &= ~NO_DWARF_UNWIND;
}
} else if (STREQ(args[optind], "off")) {
kt->flags &= ~DWARF_UNWIND;
if (!runtime)
kt->flags |= NO_DWARF_UNWIND;
} else if (IS_A_NUMBER(args[optind])) {
value = stol(args[optind],
FAULT_ON_ERROR, NULL);
if (value) {
if ((kt->flags & DWARF_UNWIND_CAPABLE) ||
!runtime) {
kt->flags |= DWARF_UNWIND;
kt->flags &= ~NO_DWARF_UNWIND;
}
} else {
kt->flags &= ~DWARF_UNWIND;
if (!runtime)
kt->flags |= NO_DWARF_UNWIND;
}
} else
goto invalid_set_command;
}
if (runtime)
fprintf(fp, "unwind: %s\n",
kt->flags & DWARF_UNWIND ? "on" : "off");
return;
} else if (STREQ(args[optind], "refresh")) {
if (args[optind+1]) {
optind++;
if (!runtime)
defer();
else if (STREQ(args[optind], "on"))
tt->flags |= TASK_REFRESH;
else if (STREQ(args[optind], "off")) {
tt->flags &= ~TASK_REFRESH;
if (!runtime)
tt->flags |= TASK_REFRESH_OFF;
} else if (IS_A_NUMBER(args[optind])) {
value = stol(args[optind],
FAULT_ON_ERROR, NULL);
if (value)
tt->flags |= TASK_REFRESH;
else {
tt->flags &= ~TASK_REFRESH;
if (!runtime)
tt->flags |=
TASK_REFRESH_OFF;
}
} else
goto invalid_set_command;
}
if (runtime)
fprintf(fp, "refresh: %s\n",
tt->flags & TASK_REFRESH ? "on" : "off");
return;
} else if (STREQ(args[optind], "gdb")) {
if (args[optind+1]) {
optind++;
if (!runtime)
defer();
else if (STREQ(args[optind], "on")) {
if (pc->flags & MINIMAL_MODE)
goto invalid_set_command;
else
pc->flags2 |= GDB_CMD_MODE;
} else if (STREQ(args[optind], "off"))
pc->flags2 &= ~GDB_CMD_MODE;
else if (IS_A_NUMBER(args[optind])) {
value = stol(args[optind],
FAULT_ON_ERROR, NULL);
if (value) {
if (pc->flags & MINIMAL_MODE)
goto invalid_set_command;
else
pc->flags2 |= GDB_CMD_MODE;
} else
pc->flags2 &= ~GDB_CMD_MODE;
} else
goto invalid_set_command;
set_command_prompt(pc->flags2 & GDB_CMD_MODE ?
"gdb> " : NULL);
}
if (runtime)
fprintf(fp, "gdb: %s\n",
pc->flags2 & GDB_CMD_MODE ? "on" : "off");
return;
} else if (STREQ(args[optind], "scroll")) {
if (args[optind+1] && pc->scroll_command) {
optind++;
if (from_rc_file)
already_done();
else if (STREQ(args[optind], "on"))
pc->flags |= SCROLL;
else if (STREQ(args[optind], "off"))
pc->flags &= ~SCROLL;
else if (STREQ(args[optind], "more"))
pc->scroll_command = SCROLL_MORE;
else if (STREQ(args[optind], "less"))
pc->scroll_command = SCROLL_LESS;
else if (STREQ(args[optind], "CRASHPAGER")) {
if (CRASHPAGER_valid())
pc->scroll_command = SCROLL_CRASHPAGER;
} else if (IS_A_NUMBER(args[optind])) {
value = stol(args[optind],
FAULT_ON_ERROR, NULL);
if (value)
pc->flags |= SCROLL;
else
pc->flags &= ~SCROLL;
} else
goto invalid_set_command;
}
if (runtime) {
fprintf(fp, "scroll: %s ",
pc->flags & SCROLL ? "on" : "off");
switch (pc->scroll_command)
{
case SCROLL_LESS:
fprintf(fp, "(/usr/bin/less)\n");
break;
case SCROLL_MORE:
fprintf(fp, "(/bin/more)\n");
break;
case SCROLL_NONE:
fprintf(fp, "(none)\n");
break;
case SCROLL_CRASHPAGER:
fprintf(fp, "(CRASHPAGER: %s)\n", getenv("CRASHPAGER"));
break;
}
}
return;
} else if (STREQ(args[optind], "silent")) {
if (args[optind+1]) {
optind++;
if (STREQ(args[optind], "on")) {
pc->flags |= SILENT;
pc->flags &= ~SCROLL;
}
else if (STREQ(args[optind], "off"))
pc->flags &= ~SILENT;
else if (IS_A_NUMBER(args[optind])) {
value = stol(args[optind],
FAULT_ON_ERROR, NULL);
if (value) {
pc->flags |= SILENT;
pc->flags &= ~SCROLL;
}
else
pc->flags &= ~SILENT;
} else
goto invalid_set_command;
if (!(pc->flags & SILENT))
fprintf(fp, "silent: off\n");
} else if (runtime && !(pc->flags & SILENT))
fprintf(fp, "silent: off\n");
return;
} else if (STREQ(args[optind], "console")) {
int assignment;
if (args[optind+1]) {
create_console_device(args[optind+1]);
optind++;
assignment = optind;
} else
assignment = 0;
if (runtime) {
fprintf(fp, "console: ");
if (pc->console)
fprintf(fp, "%s\n", pc->console);
else {
if (assignment)
fprintf(fp,
"assignment to %s failed\n",
args[assignment]);
else
fprintf(fp, "not set\n");
}
}
return;
} else if (STREQ(args[optind], "core")) {
if (args[optind+1]) {
optind++;
if (STREQ(args[optind], "on"))
pc->flags |= DROP_CORE;
else if (STREQ(args[optind], "off"))
pc->flags &= ~DROP_CORE;
else if (IS_A_NUMBER(args[optind])) {
value = stol(args[optind],
FAULT_ON_ERROR, NULL);
if (value)
pc->flags |= DROP_CORE;
else
pc->flags &= ~DROP_CORE;
} else
goto invalid_set_command;
}
if (runtime) {
fprintf(fp, "core: %s on error message)\n",
pc->flags & DROP_CORE ?
"on (drop core" :
"off (do NOT drop core");
}
return;
} else if (STREQ(args[optind], "radix")) {
if (args[optind+1]) {
optind++;
if (!runtime)
defer();
else if (from_rc_file &&
(pc->flags2 & RADIX_OVERRIDE))
ignore();
else if (STREQ(args[optind], "10") ||
STRNEQ(args[optind], "dec") ||
STRNEQ(args[optind], "ten"))
pc->output_radix = 10;
else if (STREQ(args[optind], "16") ||
STRNEQ(args[optind], "hex") ||
STRNEQ(args[optind], "six"))
pc->output_radix = 16;
else
goto invalid_set_command;
}
if (runtime) {
sprintf(buf, "set output-radix %d",
pc->output_radix);
gdb_pass_through(buf, NULL, GNU_FROM_TTY_OFF);
fprintf(fp, "output radix: %d (%s)\n",
pc->output_radix,
pc->output_radix == 10 ?
"decimal" : "hex");
}
return;
} else if (STREQ(args[optind], "hex")) {
if (from_rc_file && (pc->flags2 & RADIX_OVERRIDE))
ignore();
else if (runtime) {
pc->output_radix = 16;
gdb_pass_through("set output-radix 16",
NULL, GNU_FROM_TTY_OFF);
fprintf(fp, "output radix: 16 (hex)\n");
}
return;
} else if (STREQ(args[optind], "dec")) {
if (from_rc_file && (pc->flags2 & RADIX_OVERRIDE))
ignore();
else if (runtime) {
pc->output_radix = 10;
gdb_pass_through("set output-radix 10",
NULL, GNU_FROM_TTY_OFF);
fprintf(fp, "output radix: 10 (decimal)\n");
}
return;
} else if (STREQ(args[optind], "edit")) {
if (args[optind+1]) {
if (runtime && !from_rc_file)
error(FATAL,
"cannot change editing mode during runtime\n");
optind++;
if (from_rc_file)
already_done();
else if (STREQ(args[optind], "vi"))
pc->editing_mode = "vi";
else if (STREQ(args[optind], "emacs"))
pc->editing_mode = "emacs";
else
goto invalid_set_command;
}
if (runtime)
fprintf(fp, "edit: %s\n", pc->editing_mode);
return;
} else if (STREQ(args[optind], "vi")) {
if (runtime) {
if (!from_rc_file)
error(FATAL,
"cannot change editing mode during runtime\n");
fprintf(fp, "edit: %s\n", pc->editing_mode);
} else
pc->editing_mode = "vi";
return;
} else if (STREQ(args[optind], "emacs")) {
if (runtime) {
if (!from_rc_file)
error(FATAL,
"cannot change %s editing mode during runtime\n",
pc->editing_mode);
fprintf(fp, "edit: %s\n", pc->editing_mode);
} else
pc->editing_mode = "emacs";
return;
} else if (STREQ(args[optind], "print_max")) {
optind++;
if (args[optind]) {
if (!runtime)
defer();
else if (decimal(args[optind], 0))
*gdb_print_max = atoi(args[optind]);
else if (hexadecimal(args[optind], 0))
*gdb_print_max = (unsigned int)
htol(args[optind],
FAULT_ON_ERROR, NULL);
else
goto invalid_set_command;
}
if (runtime)
fprintf(fp, "print_max: %d\n", *gdb_print_max);
return;
} else if (STREQ(args[optind], "scope")) {
optind++;
if (args[optind]) {
if (!runtime)
defer();
else if (can_eval(args[optind]))
value = eval(args[optind], FAULT_ON_ERROR, NULL);
else if (hexadecimal(args[optind], 0))
value = htol(args[optind], FAULT_ON_ERROR, NULL);
else if ((sp = symbol_search(args[optind])))
value = sp->value;
else
goto invalid_set_command;
if (runtime) {
if (gdb_set_crash_scope(value, args[optind]))
pc->scope = value;
else
return;
}
}
if (runtime) {
fprintf(fp, "scope: %lx ", pc->scope);
if (pc->scope)
fprintf(fp, "(%s)\n",
value_to_symstr(pc->scope, buf, 0));
else
fprintf(fp, "(not set)\n");
}
return;
} else if (STREQ(args[optind], "null-stop")) {
optind++;
if (args[optind]) {
if (!runtime)
defer();
else if (STREQ(args[optind], "on"))
*gdb_stop_print_at_null = 1;
else if (STREQ(args[optind], "off"))
*gdb_stop_print_at_null = 0;
else if (IS_A_NUMBER(args[optind])) {
value = stol(args[optind],
FAULT_ON_ERROR, NULL);
if (value)
*gdb_stop_print_at_null = 1;
else
*gdb_stop_print_at_null = 0;
} else
goto invalid_set_command;
}
if (runtime)
fprintf(fp, "null-stop: %s\n",
*gdb_stop_print_at_null ? "on" : "off");
return;
} else if (STREQ(args[optind], "print_array")) {
optind++;
if (args[optind]) {
if (!runtime)
defer();
else if (STREQ(args[optind], "on"))
*gdb_prettyprint_arrays = 1;
else if (STREQ(args[optind], "off"))
*gdb_prettyprint_arrays = 0;
else if (IS_A_NUMBER(args[optind])) {
value = stol(args[optind],
FAULT_ON_ERROR, NULL);
if (value)
*gdb_prettyprint_arrays = 1;
else
*gdb_prettyprint_arrays = 0;
} else
goto invalid_set_command;
}
if (runtime)
fprintf(fp, "print_array: %s\n",
*gdb_prettyprint_arrays ? "on" : "off");
return;
} else if (STREQ(args[optind], "namelist")) {
optind++;
if (!runtime && args[optind]) {
if (!is_elf_file(args[optind]))
error(FATAL,
"%s: not a kernel namelist (from .%src file)\n",
args[optind],
pc->program_name);
if ((pc->namelist = (char *)
malloc(strlen(args[optind])+1)) == NULL) {
error(INFO,
"cannot malloc memory for namelist: %s: %s\n",
args[optind], strerror(errno));
} else
strcpy(pc->namelist, args[optind]);
}
if (runtime)
fprintf(fp, "namelist: %s\n", pc->namelist);
return;
} else if (STREQ(args[optind], "free")) {
if (!runtime)
defer();
else
fprintf(fp, "%d pages freed\n",
dumpfile_memory(DUMPFILE_FREE_MEM));
return;
} else if (STREQ(args[optind], "data_debug")) {
pc->flags |= DATADEBUG;
return;
} else if (STREQ(args[optind], "zero_excluded")) {
if (args[optind+1]) {
optind++;
if (from_rc_file)
already_done();
else if (STREQ(args[optind], "on")) {
*diskdump_flags |= ZERO_EXCLUDED;
sadump_set_zero_excluded();
} else if (STREQ(args[optind], "off")) {
*diskdump_flags &= ~ZERO_EXCLUDED;
sadump_unset_zero_excluded();
} else if (IS_A_NUMBER(args[optind])) {
value = stol(args[optind],
FAULT_ON_ERROR, NULL);
if (value) {
*diskdump_flags |= ZERO_EXCLUDED;
sadump_set_zero_excluded();
} else {
*diskdump_flags &= ~ZERO_EXCLUDED;
sadump_unset_zero_excluded();
}
} else
goto invalid_set_command;
}
if (runtime)
fprintf(fp, "zero_excluded: %s\n",
(*diskdump_flags & ZERO_EXCLUDED) ||
sadump_is_zero_excluded() ?
"on" : "off");
return;
} else if (STREQ(args[optind], "offline")) {
if (args[optind+1]) {
optind++;
if (from_rc_file)
already_done();
else if (STREQ(args[optind], "show"))
pc->flags2 &= ~OFFLINE_HIDE;
else if(STREQ(args[optind], "hide"))
pc->flags2 |= OFFLINE_HIDE;
else
goto invalid_set_command;
}
if (runtime)
fprintf(fp, " offline: %s\n",
pc->flags2 & OFFLINE_HIDE ? "hide" : "show");
return;
} else if (STREQ(args[optind], "redzone")) {
if (args[optind+1]) {
optind++;
if (STREQ(args[optind], "on"))
pc->flags2 |= REDZONE;
else if (STREQ(args[optind], "off"))
pc->flags2 &= ~REDZONE;
else if (IS_A_NUMBER(args[optind])) {
value = stol(args[optind],
FAULT_ON_ERROR, NULL);
if (value)
pc->flags2 |= REDZONE;
else
pc->flags2 &= ~REDZONE;
} else
goto invalid_set_command;
}
if (runtime) {
fprintf(fp, "redzone: %s\n",
pc->flags2 & REDZONE ?
"on" : "off");
}
return;
} else if (STREQ(args[optind], "error")) {
if (args[optind+1]) {
optind++;
if (!set_error(args[optind]))
return;
}
if (runtime) {
fprintf(fp, "error: %s\n",
pc->error_path);
}
return;
} else if (XEN_HYPER_MODE()) {
error(FATAL, "invalid argument for the Xen hypervisor\n");
} else if (pc->flags & MINIMAL_MODE) {
error(FATAL, "invalid argument in minimal mode\n");
} else if (runtime) {
ulong pid, task;
switch (str_to_context(args[optind], &value, &tc))
{
case STR_PID:
pid = value;
task = NO_TASK;
if (set_context(task, pid, TRUE))
show_context(CURRENT_CONTEXT());
break;
case STR_TASK:
task = value;
pid = NO_PID;
if (set_context(task, pid, TRUE))
show_context(CURRENT_CONTEXT());
break;
case STR_INVALID:
error(INFO, "invalid task or pid value: %s\n",
args[optind]);
break;
}
} else
console("set: ignoring \"%s\"\n", args[optind]);
optind++;
}
return;
invalid_set_command:
sprintf(buf, "invalid command");
if (!runtime)
sprintf(&buf[strlen(buf)], " in .%src file", pc->program_name);
strcat(buf, ": ");
for (i = 0; i < argcnt; i++)
sprintf(&buf[strlen(buf)], "%s ", args[i]);
strcat(buf, "\n");
if (extra_message)
strcat(buf, extra_message);
error(runtime ? FATAL : INFO, buf);
#undef defer
#undef already_done
#undef ignore
}
/*
* Display the set of settable internal variables.
*/
static void
show_options(void)
{
char buf[BUFSIZE];
fprintf(fp, " scroll: %s ",
pc->flags & SCROLL ? "on" : "off");
switch (pc->scroll_command)
{
case SCROLL_LESS:
fprintf(fp, "(/usr/bin/less)\n");
break;
case SCROLL_MORE:
fprintf(fp, "(/bin/more)\n");
break;
case SCROLL_NONE:
fprintf(fp, "(none)\n");
break;
case SCROLL_CRASHPAGER:
fprintf(fp, "(CRASHPAGER: %s)\n", getenv("CRASHPAGER"));
break;
}
fprintf(fp, " radix: %d (%s)\n", pc->output_radix,
pc->output_radix == 10 ? "decimal" :
pc->output_radix == 16 ? "hexadecimal" : "unknown");
fprintf(fp, " refresh: %s\n", tt->flags & TASK_REFRESH ? "on" : "off");
fprintf(fp, " print_max: %d\n", *gdb_print_max);
fprintf(fp, " print_array: %s\n", *gdb_prettyprint_arrays ? "on" : "off");
fprintf(fp, " console: %s\n", pc->console ?
pc->console : "(not assigned)");
fprintf(fp, " debug: %ld\n", pc->debug);
fprintf(fp, " core: %s\n", pc->flags & DROP_CORE ? "on" : "off");
fprintf(fp, " hash: %s\n", pc->flags & HASH ? "on" : "off");
fprintf(fp, " silent: %s\n", pc->flags & SILENT ? "on" : "off");
fprintf(fp, " edit: %s\n", pc->editing_mode);
fprintf(fp, " namelist: %s\n", pc->namelist);
fprintf(fp, " dumpfile: %s\n", pc->dumpfile);
fprintf(fp, " unwind: %s\n", kt->flags & DWARF_UNWIND ? "on" : "off");
fprintf(fp, " zero_excluded: %s\n",
(*diskdump_flags & ZERO_EXCLUDED) || sadump_is_zero_excluded() ?
"on" : "off");
fprintf(fp, " null-stop: %s\n", *gdb_stop_print_at_null ? "on" : "off");
fprintf(fp, " gdb: %s\n", pc->flags2 & GDB_CMD_MODE ? "on" : "off");
fprintf(fp, " scope: %lx ", pc->scope);
if (pc->scope)
fprintf(fp, "(%s)\n", value_to_symstr(pc->scope, buf, 0));
else
fprintf(fp, "(not set)\n");
fprintf(fp, " offline: %s\n", pc->flags2 & OFFLINE_HIDE ? "hide" : "show");
fprintf(fp, " redzone: %s\n", pc->flags2 & REDZONE ? "on" : "off");
fprintf(fp, " error: %s\n", pc->error_path);
}
/*
* Evaluate an expression, which can consist of a single symbol, single value,
* or an expression consisting of two values and an operator. If the
* expression contains redirection characters, the whole expression must
* be enclosed with parentheses. The result is printed in decimal, hex,
* octal and binary. Input number values can only be hex or decimal, with
* a bias towards decimal (use 0x when necessary).
*/
void
cmd_eval(void)
{
int flags;
int bitflag, longlongflag, longlongflagforce;
struct number_option nopt;
char buf1[BUFSIZE];
/*
* getopt() is not used to avoid confusion with minus sign.
*/
optind = 1;
bitflag = 0;
longlongflag = longlongflagforce = 0;
BZERO(&nopt, sizeof(struct number_option));
if (STREQ(args[optind], "-lb") || STREQ(args[optind], "-bl")) {
longlongflagforce++;
bitflag++;
optind++;
} else if (STREQ(args[optind], "-l")) {
longlongflagforce++;
optind++;
if (STREQ(args[optind], "-b") && args[optind+1]) {
optind++;
bitflag++;
}
} else if (STREQ(args[optind], "-b")) {
if (STREQ(args[optind+1], "-l")) {
if (args[optind+2]) {
bitflag++;
longlongflagforce++;
optind += 2;
} else
cmd_usage(pc->curcmd, SYNOPSIS);
} else if (args[optind+1]) {
bitflag++;
optind++;
}
}
if (!args[optind])
cmd_usage(pc->curcmd, SYNOPSIS);
longlongflag = BITS32() ? TRUE : FALSE;
flags = longlongflag ? (LONG_LONG|RETURN_ON_ERROR) : FAULT_ON_ERROR;
if(!BITS32())
longlongflagforce = 0;
BZERO(buf1, BUFSIZE);
buf1[0] = '(';
while (args[optind]) {
if (*args[optind] == '(') {
if (eval_common(args[optind], flags, NULL, &nopt))
print_number(&nopt, bitflag, longlongflagforce);
else
error(FATAL, "invalid expression: %s\n",
args[optind]);
return;
}
else {
strcat(buf1, args[optind]);
strcat(buf1, " ");
}
optind++;
}
clean_line(buf1);
strcat(buf1, ")");
if (eval_common(buf1, flags, NULL, &nopt))
print_number(&nopt, bitflag, longlongflagforce);
else
error(FATAL, "invalid expression: %s\n", buf1);
}
/*
* Pre-check a string for eval-worthiness. This allows callers to avoid
* having to encompass a non-whitespace expression with parentheses.
* Note that the data being evaluated is not error-checked here, but
* rather that it exists in the proper format.
*/
int
can_eval(char *s)
{
char *op;
char *element1, *element2;
char work[BUFSIZE];
/*
* If we've got a () pair containing any sort of stuff in between,
* then presume it's eval-able. It might contain crap, but it
* should be sent to eval() regardless.
*/
if ((FIRSTCHAR(s) == '(') &&
(count_chars(s, '(') == 1) &&
(count_chars(s, ')') == 1) &&
(strlen(s) > 2) &&
(LASTCHAR(s) == ')'))
return TRUE;
/*
* If the string contains any of the operators except the shifters,
* and has any kind of data on either side, it's also eval-able.
*/
strcpy(work, s);
if (!(op = strpbrk(work, "><+-&|*/%^")))
return FALSE;
element1 = &work[0];
*op = NULLCHAR;
element2 = op+1;
if (!strlen(element1) || !strlen(element2))
return FALSE;
return TRUE;
}
/*
* Evaluate an expression involving two values and an operator.
*/
#define OP_ADD (1)
#define OP_SUB (2)
#define OP_AND (3)
#define OP_OR (4)
#define OP_MUL (5)
#define OP_DIV (6)
#define OP_MOD (7)
#define OP_SL (8)
#define OP_SR (9)
#define OP_EXOR (10)
#define OP_POWER (11)
ulong
eval(char *s, int flags, int *errptr)
{
struct number_option nopt;
if (eval_common(s, flags, errptr, &nopt)) {
return(nopt.num);
} else {
switch (flags & (FAULT_ON_ERROR|RETURN_ON_ERROR))
{
case FAULT_ON_ERROR:
error(FATAL, "invalid expression: %s\n", s);
case RETURN_ON_ERROR:
error(INFO, "invalid expression: %s\n", s);
if (errptr)
*errptr = TRUE;
break;
}
return UNUSED;
}
}
ulonglong
evall(char *s, int flags, int *errptr)
{
struct number_option nopt;
if (BITS32())
flags |= LONG_LONG;
if (eval_common(s, flags, errptr, &nopt)) {
return(nopt.ll_num);
} else {
switch (flags & (FAULT_ON_ERROR|RETURN_ON_ERROR))
{
case FAULT_ON_ERROR:
error(FATAL, "invalid expression: %s\n", s);
case RETURN_ON_ERROR:
error(INFO, "invalid expression: %s\n", s);
if (errptr)
*errptr = TRUE;
break;
}
return UNUSED;
}
}
int
eval_common(char *s, int flags, int *errptr, struct number_option *np)
{
char *p1, *p2;
char *op, opcode;
ulong value1;
ulong value2;
ulonglong ll_value1;
ulonglong ll_value2;
char work[BUFSIZE];
char *element1;
char *element2;
struct syment *sp;
opcode = 0;
value1 = value2 = 0;
ll_value1 = ll_value2 = 0;
if (strstr(s, "(") || strstr(s, ")")) {
p1 = s;
if (*p1 != '(')
goto malformed;
if (LASTCHAR(s) != ')')
goto malformed;
p2 = &LASTCHAR(s);
if (strstr(s, ")") != p2)
goto malformed;
strcpy(work, p1+1);
LASTCHAR(work) = NULLCHAR;
if (strstr(work, "(") || strstr(work, ")"))
goto malformed;
} else
strcpy(work, s);
if (work[0] == '-') {
shift_string_right(work, 1);
work[0] = '0';
}
if (!(op = strpbrk(work, "#><+-&|*/%^"))) {
if (calculate(work, &value1, &ll_value1,
flags & (HEX_BIAS|LONG_LONG))) {
if (flags & LONG_LONG) {
np->ll_num = ll_value1;
if (BITS32() && (ll_value1 > 0xffffffff))
np->retflags |= LONG_LONG;
return TRUE;
} else {
np->num = value1;
return TRUE;
}
}
goto malformed;
}
switch (*op)
{
case '+':
opcode = OP_ADD;
break;
case '-':
opcode = OP_SUB;
break;
case '&':
opcode = OP_AND;
break;
case '|':
opcode = OP_OR;
break;
case '*':
opcode = OP_MUL;
break;
case '%':
opcode = OP_MOD;
break;
case '/':
opcode = OP_DIV;
break;
case '<':
if (*(op+1) != '<')
goto malformed;
opcode = OP_SL;
break;
case '>':
if (*(op+1) != '>')
goto malformed;
opcode = OP_SR;
break;
case '^':
opcode = OP_EXOR;
break;
case '#':
opcode = OP_POWER;
break;
}
element1 = &work[0];
*op = NULLCHAR;
if ((opcode == OP_SL) || (opcode == OP_SR)) {
*(op+1) = NULLCHAR;
element2 = op+2;
} else
element2 = op+1;
if (strlen(clean_line(element1)) == 0)
goto malformed;
if (strlen(clean_line(element2)) == 0)
goto malformed;
if ((sp = symbol_search(element1)))
value1 = ll_value1 = sp->value;
else {
if (!calculate(element1, &value1, &ll_value1,
flags & (HEX_BIAS|LONG_LONG)))
goto malformed;
if (BITS32() && (ll_value1 > 0xffffffff))
np->retflags |= LONG_LONG;
}
if ((sp = symbol_search(element2)))
value2 = ll_value2 = sp->value;
else if (!calculate(element2, &value2, &ll_value2,
flags & (HEX_BIAS|LONG_LONG)))
goto malformed;
if (flags & LONG_LONG) {
if (BITS32() && (ll_value2 > 0xffffffff))
np->retflags |= LONG_LONG;
switch (opcode)
{
case OP_ADD:
np->ll_num = (ll_value1 + ll_value2);
break;
case OP_SUB:
np->ll_num = (ll_value1 - ll_value2);
break;
case OP_AND:
np->ll_num = (ll_value1 & ll_value2);
break;
case OP_OR:
np->ll_num = (ll_value1 | ll_value2);
break;
case OP_MUL:
np->ll_num = (ll_value1 * ll_value2);
break;
case OP_DIV:
np->ll_num = (ll_value1 / ll_value2);
break;
case OP_MOD:
np->ll_num = (ll_value1 % ll_value2);
break;
case OP_SL:
np->ll_num = (ll_value1 << ll_value2);
break;
case OP_SR:
np->ll_num = (ll_value1 >> ll_value2);
break;
case OP_EXOR:
np->ll_num = (ll_value1 ^ ll_value2);
break;
case OP_POWER:
np->ll_num = ll_power(ll_value1, ll_value2);
break;
}
} else {
switch (opcode)
{
case OP_ADD:
np->num = (value1 + value2);
break;
case OP_SUB:
np->num = (value1 - value2);
break;
case OP_AND:
np->num = (value1 & value2);
break;
case OP_OR:
np->num = (value1 | value2);
break;
case OP_MUL:
np->num = (value1 * value2);
break;
case OP_DIV:
np->num = (value1 / value2);
break;
case OP_MOD:
np->num = (value1 % value2);
break;
case OP_SL:
np->num = (value1 << value2);
break;
case OP_SR:
np->num = (value1 >> value2);
break;
case OP_EXOR:
np->num = (value1 ^ value2);
break;
case OP_POWER:
np->num = power(value1, value2);
break;
}
}
return TRUE;
malformed:
return FALSE;
}
/*
* Take string containing a number, and possibly a multiplier, and calculate
* its real value. The allowable multipliers are k, K, m, M, g and G, for
* kilobytes, megabytes and gigabytes.
*/
int
calculate(char *s, ulong *value, ulonglong *llvalue, ulong flags)
{
ulong factor, bias;
int errflag;
int ones_complement;
ulong localval;
ulonglong ll_localval;
struct syment *sp;
bias = flags & HEX_BIAS;
if (*s == '~') {
ones_complement = TRUE;
s++;
} else
ones_complement = FALSE;
if ((sp = symbol_search(s))) {
if (flags & LONG_LONG) {
*llvalue = (ulonglong)sp->value;
if (ones_complement)
*llvalue = ~(*llvalue);
} else
*value = ones_complement ? ~(sp->value) : sp->value;
return TRUE;
}
factor = 1;
errflag = 0;
switch (LASTCHAR(s))
{
case 'k':
case 'K':
LASTCHAR(s) = NULLCHAR;
if (IS_A_NUMBER(s))
factor = 1024;
else
return FALSE;
break;
case 'm':
case 'M':
LASTCHAR(s) = NULLCHAR;
if (IS_A_NUMBER(s))
factor = (1024*1024);
else
return FALSE;
break;
case 'g':
case 'G':
LASTCHAR(s) = NULLCHAR;
if (IS_A_NUMBER(s))
factor = (1024*1024*1024);
else
return FALSE;
break;
default:
if (!IS_A_NUMBER(s))
return FALSE;
break;
}
if (flags & LONG_LONG) {
ll_localval = stoll(s, RETURN_ON_ERROR|bias, &errflag);
if (errflag)
return FALSE;
if (ones_complement)
*llvalue = ~(ll_localval * factor);
else
*llvalue = ll_localval * factor;
} else {
localval = stol(s, RETURN_ON_ERROR|bias, &errflag);
if (errflag)
return FALSE;
if (ones_complement)
*value = ~(localval * factor);
else
*value = localval * factor;
}
return TRUE;
}
/*
* Print a 32-bit or 64-bit number in hexadecimal, decimal, octal and binary,
* also showing the bits set if appropriate.
*
*/
static void
print_number(struct number_option *np, int bitflag, int longlongflagforce)
{
int i;
ulong hibit;
ulonglong ll_hibit;
int ccnt;
ulong mask;
ulonglong ll_mask;
char *hdr = " bits set: ";
char buf[BUFSIZE];
int hdrlen;
int longlongformat;
longlongformat = longlongflagforce;
if (!longlongflagforce) {
if (BITS32()) {
if (np->retflags & LONG_LONG)
longlongformat = TRUE;
if (np->ll_num > 0xffffffff)
longlongformat = TRUE;
else
np->num = (ulong)np->ll_num;
}
}
if (longlongformat) {
ll_hibit = (ulonglong)(1) << ((sizeof(long long)*8)-1);
fprintf(fp, "hexadecimal: %llx ", np->ll_num);
if (np->ll_num >= KILOBYTES(1)) {
if ((np->ll_num % GIGABYTES(1)) == 0)
fprintf(fp, "(%lldGB)",
np->ll_num / GIGABYTES(1));
else if ((np->ll_num % MEGABYTES(1)) == 0)
fprintf(fp, "(%lldMB)",
np->ll_num / MEGABYTES(1));
else if ((np->ll_num % KILOBYTES(1)) == 0)
fprintf(fp, "(%lldKB)",
np->ll_num / KILOBYTES(1));
}
fprintf(fp, "\n");
fprintf(fp, " decimal: %llu ", np->ll_num);
if ((long long)np->ll_num < 0)
fprintf(fp, "(%lld)\n", (long long)np->ll_num);
else
fprintf(fp, "\n");
fprintf(fp, " octal: %llo\n", np->ll_num);
fprintf(fp, " binary: ");
for(i = 0, ll_mask = np->ll_num; i < (sizeof(long long)*8);
i++, ll_mask <<= 1)
if (ll_mask & ll_hibit)
fprintf(fp, "1");
else
fprintf(fp, "0");
fprintf(fp,"\n");
} else {
hibit = (ulong)(1) << ((sizeof(long)*8)-1);
fprintf(fp, "hexadecimal: %lx ", np->num);
if (np->num >= KILOBYTES(1)) {
if ((np->num % GIGABYTES(1)) == 0)
fprintf(fp, "(%ldGB)", np->num / GIGABYTES(1));
else if ((np->num % MEGABYTES(1)) == 0)
fprintf(fp, "(%ldMB)", np->num / MEGABYTES(1));
else if ((np->num % KILOBYTES(1)) == 0)
fprintf(fp, "(%ldKB)", np->num / KILOBYTES(1));
}
fprintf(fp, "\n");
fprintf(fp, " decimal: %lu ", np->num);
if ((long)np->num < 0)
fprintf(fp, "(%ld)\n", (long)np->num);
else
fprintf(fp, "\n");
fprintf(fp, " octal: %lo\n", np->num);
fprintf(fp, " binary: ");
for(i = 0, mask = np->num; i < (sizeof(long)*8);
i++, mask <<= 1)
if (mask & hibit)
fprintf(fp, "1");
else
fprintf(fp, "0");
fprintf(fp,"\n");
}
if (!bitflag)
return;
hdrlen = strlen(hdr);
ccnt = hdrlen;
fprintf(fp, "%s", hdr);
if (longlongformat) {
for (i = 63; i >= 0; i--) {
ll_mask = (ulonglong)(1) << i;
if (np->ll_num & ll_mask) {
sprintf(buf, "%d ", i);
fprintf(fp, "%s", buf);
ccnt += strlen(buf);
if (ccnt >= 77) {
fprintf(fp, "\n");
INDENT(strlen(hdr));
ccnt = hdrlen;
}
}
}
} else {
for (i = BITS()-1; i >= 0; i--) {
mask = (ulong)(1) << i;
if (np->num & mask) {
sprintf(buf, "%d ", i);
fprintf(fp, "%s", buf);
ccnt += strlen(buf);
if (ccnt >= 77) {
fprintf(fp, "\n");
INDENT(strlen(hdr));
ccnt = hdrlen;
}
}
}
}
fprintf(fp, "\n");
}
/*
* Display the contents of a linked list. Minimum requirements are a starting
* address, typically of a structure which contains the "next" list entry at
* some offset into the structure. The default offset is zero bytes, and need
* not be entered if that's the case. Otherwise a number argument that's not
* a kernel * virtual address will be understood to be the offset.
* Alternatively the offset may be entered in "struct.member" format. Each
* item in the list is dumped, and the list will be considered terminated upon
* encountering a "next" value that is:
*
* a NULL pointer.
* a pointer to the starting address.
* a pointer to the entry pointed to by the starting address.
* a pointer to the structure itself.
* a pointer to the value specified with the "-e ending_addr" option.
*
* If the structures are linked using list_head structures, the -h or -H
* options must be used. In that case, the "start" address is:
* a pointer to the structure that contains the list_head structure (-h),
* or a pointer to a LIST_HEAD() structure (-H).
*
* Given that the contents of the structures containing the next pointers
* often contain useful data, the "-s structname" also prints each structure
* in the list.
*
* By default, the list members are hashed to guard against duplicate entries
* causing the list to wrap back upon itself.
*
* WARNING: There's an inordinate amount of work parsing arguments below
* in order to maintain backwards compatibility re: not having to use -o,
* which gets sticky with zero-based kernel virtual address space.
*/
void
cmd_list(void)
{
int c;
long head_member_offset = 0; /* offset for head like denty.d_subdirs */
struct list_data list_data, *ld;
struct datatype_member struct_member, *sm;
struct syment *sp;
ulong value, struct_list_offset;
sm = &struct_member;
ld = &list_data;
BZERO(ld, sizeof(struct list_data));
struct_list_offset = 0;
while ((c = getopt(argcnt, args, "BHhrs:S:e:o:O:xdl:")) != EOF) {
switch(c)
{
case 'B':
ld->flags |= LIST_BRENT_ALGO;
break;
case 'H':
ld->flags |= LIST_HEAD_FORMAT;
ld->flags |= LIST_HEAD_POINTER;
break;
case 'h':
ld->flags |= LIST_HEAD_FORMAT;
break;
case 'r':
ld->flags |= LIST_HEAD_FORMAT;
ld->flags |= LIST_HEAD_REVERSE;
break;
case 's':
case 'S':
if (ld->structname_args++ == 0)
hq_open();
hq_enter((ulong)optarg);
ld->flags |= (c == 's') ? LIST_PARSE_MEMBER : LIST_READ_MEMBER;
if (count_bits_long(ld->flags & (LIST_PARSE_MEMBER|LIST_READ_MEMBER)) > 1)
error(FATAL, "-S and -s options are mutually exclusive\n");
break;
case 'l':
if (IS_A_NUMBER(optarg))
struct_list_offset = stol(optarg,
FAULT_ON_ERROR, NULL);
else if (arg_to_datatype(optarg,
sm, RETURN_ON_ERROR) > 1)
struct_list_offset = sm->member_offset;
else
error(FATAL, "invalid -l option: %s\n",
optarg);
break;
case 'O':
if (ld->flags & LIST_HEAD_OFFSET_ENTERED)
error(FATAL, "offset value %d (0x%lx) already entered\n",
head_member_offset, head_member_offset);
else if (IS_A_NUMBER(optarg))
head_member_offset = stol(optarg, FAULT_ON_ERROR, NULL);
else if (arg_to_datatype(optarg, sm, RETURN_ON_ERROR) > 1)
head_member_offset = sm->member_offset;
else
error(FATAL, "invalid -O argument: %s\n", optarg);
ld->flags |= LIST_HEAD_OFFSET_ENTERED;
break;
case 'o':
if (ld->flags & LIST_OFFSET_ENTERED)
error(FATAL,
"offset value %d (0x%lx) already entered\n",
ld->member_offset, ld->member_offset);
else if (IS_A_NUMBER(optarg))
ld->member_offset = stol(optarg,
FAULT_ON_ERROR, NULL);
else if (arg_to_datatype(optarg,
sm, RETURN_ON_ERROR) > 1)
ld->member_offset = sm->member_offset;
else
error(FATAL, "invalid -o argument: %s\n",
optarg);
ld->flags |= LIST_OFFSET_ENTERED;
break;
case 'e':
ld->end = htol(optarg, FAULT_ON_ERROR, NULL);
break;
case 'x':
if (ld->flags & LIST_STRUCT_RADIX_10)
error(FATAL,
"-d and -x are mutually exclusive\n");
ld->flags |= LIST_STRUCT_RADIX_16;
break;
case 'd':
if (ld->flags & LIST_STRUCT_RADIX_16)
error(FATAL,
"-d and -x are mutually exclusive\n");
ld->flags |= LIST_STRUCT_RADIX_10;
break;
default:
argerrs++;
break;
}
}
if (argerrs)
cmd_usage(pc->curcmd, SYNOPSIS);
if (args[optind] && args[optind+1] && args[optind+2]) {
error(INFO, "too many arguments\n");
cmd_usage(pc->curcmd, SYNOPSIS);
}
if (ld->structname_args) {
ld->structname = (char **)GETBUF(sizeof(char *) * ld->structname_args);
retrieve_list((ulong *)ld->structname, ld->structname_args);
hq_close();
ld->struct_list_offset = struct_list_offset;
} else if (struct_list_offset) {
error(INFO, "-l option can only be used with -s or -S option\n");
cmd_usage(pc->curcmd, SYNOPSIS);
}
while (args[optind]) {
if (strstr(args[optind], ".") &&
arg_to_datatype(args[optind], sm, RETURN_ON_ERROR) > 1) {
if (ld->flags & LIST_OFFSET_ENTERED)
error(FATAL,
"offset value %ld (0x%lx) already entered\n",
ld->member_offset, ld->member_offset);
ld->member_offset = sm->member_offset;
ld->flags |= LIST_OFFSET_ENTERED;
} else {
/*
* Do an inordinate amount of work to avoid -o...
*
* OK, if it's a symbol, then it has to be a start.
*/
if ((sp = symbol_search(args[optind]))) {
if (ld->flags & LIST_START_ENTERED)
error(FATAL,
"list start already entered\n");
ld->start = sp->value;
ld->flags |= LIST_START_ENTERED;
goto next_arg;
}
/*
* If it's not a symbol nor a number, bail out if it
* cannot be evaluated as a start address.
*/
if (!IS_A_NUMBER(args[optind])) {
if (can_eval(args[optind])) {
value = eval(args[optind], FAULT_ON_ERROR, NULL);
if (IS_KVADDR(value)) {
if (ld->flags & LIST_START_ENTERED)
error(FATAL,
"list start already entered\n");
ld->start = value;
ld->flags |= LIST_START_ENTERED;
goto next_arg;
}
}
error(FATAL, "invalid argument: %s\n",
args[optind]);
}
/*
* If the start is known, it's got to be an offset.
*/
if (ld->flags & LIST_START_ENTERED) {
value = stol(args[optind], FAULT_ON_ERROR,
NULL);
ld->member_offset = value;
ld->flags |= LIST_OFFSET_ENTERED;
break;
}
/*
* If the offset is known, or there's no subsequent
* argument, then it's got to be a start.
*/
if ((ld->flags & LIST_OFFSET_ENTERED) ||
!args[optind+1]) {
value = htol(args[optind], FAULT_ON_ERROR,
NULL);
if (!IS_KVADDR(value))
error(FATAL,
"invalid kernel virtual address: %s\n",
args[optind]);
ld->start = value;
ld->flags |= LIST_START_ENTERED;
break;
}
/*
* Neither start nor offset has been entered, and
* it's a number. Look ahead to the next argument.
* If it's a symbol, then this must be an offset.
*/
if ((sp = symbol_search(args[optind+1]))) {
value = stol(args[optind], FAULT_ON_ERROR,
NULL);
ld->member_offset = value;
ld->flags |= LIST_OFFSET_ENTERED;
goto next_arg;
} else if ((!IS_A_NUMBER(args[optind+1]) &&
!can_eval(args[optind+1])) &&
!strstr(args[optind+1], "."))
error(FATAL, "symbol not found: %s\n",
args[optind+1]);
/*
* Crunch time. We've got two numbers. If they're
* both ambigous we must have zero-based kernel
* virtual address space.
*/
if (COMMON_VADDR_SPACE() &&
AMBIGUOUS_NUMBER(args[optind]) &&
AMBIGUOUS_NUMBER(args[optind+1])) {
error(INFO,
"ambiguous arguments: \"%s\" and \"%s\": -o is required\n",
args[optind], args[optind+1]);
cmd_usage(pc->curcmd, SYNOPSIS);
}
if (hexadecimal_only(args[optind], 0)) {
value = htol(args[optind], FAULT_ON_ERROR,
NULL);
if (IS_KVADDR(value)) {
ld->start = value;
ld->flags |= LIST_START_ENTERED;
goto next_arg;
}
}
value = stol(args[optind], FAULT_ON_ERROR, NULL);
ld->member_offset = value;
ld->flags |= LIST_OFFSET_ENTERED;
}
next_arg:
optind++;
}
if (!(ld->flags & LIST_START_ENTERED)) {
error(INFO, "starting address required\n");
cmd_usage(pc->curcmd, SYNOPSIS);
}
if ((ld->flags & LIST_OFFSET_ENTERED) && ld->struct_list_offset) {
error(INFO, "-l and -o are mutually exclusive\n");
cmd_usage(pc->curcmd, SYNOPSIS);
}
if (ld->flags & LIST_HEAD_FORMAT) {
ld->list_head_offset = ld->member_offset;
if (ld->flags & LIST_HEAD_REVERSE)
ld->member_offset = sizeof(void *);
else
ld->member_offset = 0;
if (ld->flags & LIST_HEAD_POINTER) {
if (!ld->end)
ld->end = ld->start;
readmem(ld->start + ld->member_offset, KVADDR, &ld->start,
sizeof(void *), "LIST_HEAD contents", FAULT_ON_ERROR);
if (ld->start == ld->end) {
fprintf(fp, "(empty)\n");
return;
}
} else {
if (ld->flags & LIST_HEAD_OFFSET_ENTERED) {
if (!ld->end)
ld->end = ld->start + head_member_offset;
readmem(ld->start + head_member_offset, KVADDR, &ld->start,
sizeof(void *), "LIST_HEAD contents", FAULT_ON_ERROR);
if (ld->start == ld->end) {
fprintf(fp, "(empty)\n");
return;
}
} else
ld->start += ld->list_head_offset;
}
}
ld->flags &= ~(LIST_OFFSET_ENTERED|LIST_START_ENTERED);
ld->flags |= VERBOSE;
if (ld->flags & LIST_BRENT_ALGO)
c = do_list_no_hash(ld);
else {
hq_open();
c = do_list(ld);
hq_close();
}
if (ld->structname_args)
FREEBUF(ld->structname);
}
void
dump_struct_members_fast(struct req_entry *e, int radix, ulong p)
{
unsigned int i;
char b[BUFSIZE];
if (!(e && IS_KVADDR(p)))
return;
if (!radix)
radix = *gdb_output_radix;
for (i = 0; i < e->count; i++) {
if (0 < e->width[i] && (e->width[i] <= 8 || e->is_str[i])) {
print_value(e, i, p, e->is_ptr[i] ? 16 : radix);
} else if (e->width[i] == 0 || e->width[i] > 8) {
snprintf(b, BUFSIZE, "%s.%s", e->name, e->member[i]);
dump_struct_member(b, p, radix);
}
}
}
struct req_entry *
fill_member_offsets(char *arg)
{
int j;
char *p, m;
struct req_entry *e;
char buf[BUFSIZE];
if (!(arg && *arg))
return NULL;
j = count_chars(arg, ',') + 1;
e = (struct req_entry *)GETBUF(sizeof(*e));
e->arg = GETBUF(strlen(arg + 1));
strcpy(e->arg, arg);
m = ((p = strchr(e->arg, '.')) != NULL);
if (!p++)
p = e->arg + strlen(e->arg) + 1;
e->name = GETBUF(p - e->arg);
strncpy(e->name, e->arg, p - e->arg - 1);
if (!m)
return e;
e->count = count_chars(p, ',') + 1;
e->width = (ulong *)GETBUF(e->count * sizeof(ulong));
e->is_ptr = (int *)GETBUF(e->count * sizeof(int));
e->is_str = (int *)GETBUF(e->count * sizeof(int));
e->member = (char **)GETBUF(e->count * sizeof(char *));
e->offset = (ulong *)GETBUF(e->count * sizeof(ulong));
replace_string(p, ",", ' ');
parse_line(p, e->member);
for (j = 0; j < e->count; j++) {
e->offset[j] = MEMBER_OFFSET(e->name, e->member[j]);
if (e->offset[j] == INVALID_OFFSET)
e->offset[j] = ANON_MEMBER_OFFSET(e->name, e->member[j]);
if (e->offset[j] == INVALID_OFFSET)
error(FATAL, "Can't get offset of '%s.%s'\n",
e->name, e->member[j]);
e->is_ptr[j] = MEMBER_TYPE(e->name, e->member[j]) == TYPE_CODE_PTR;
e->is_str[j] = is_string(e->name, e->member[j]);
/* Dirty hack for obtaining size of particular field */
snprintf(buf, BUFSIZE, "%s + 1", e->member[j]);
e->width[j] = ANON_MEMBER_OFFSET(e->name, buf) - e->offset[j];
}
return e;
}
static void
print_value(struct req_entry *e, unsigned int i, ulong addr, unsigned int radix)
{
union { uint64_t v64; uint32_t v32;
uint16_t v16; uint8_t v8;
} v;
char buf[BUFSIZE];
struct syment *sym;
addr += e->offset[i];
/* Read up to 8 bytes, counters, pointers, etc. */
if (e->width[i] <= 8 && !readmem(addr, KVADDR, &v, e->width[i],
"structure value", RETURN_ON_ERROR | QUIET)) {
error(INFO, "cannot access member: %s at %lx\n", e->member[i], addr);
return;
}
snprintf(buf, BUFSIZE, " %%s = %s%%%s%s",
(radix == 16 ? "0x" : ""),
(e->width[i] == 8 ? "l" : ""),
(radix == 16 ? "x" : "u" )
);
switch (e->width[i]) {
case 1: fprintf(fp, buf, e->member[i], v.v8); break;
case 2: fprintf(fp, buf, e->member[i], v.v16); break;
case 4: fprintf(fp, buf, e->member[i], v.v32); break;
case 8: fprintf(fp, buf, e->member[i], v.v64); break;
}
if (e->is_str[i]) {
if (e->is_ptr[i]) {
read_string(v.v64, buf, BUFSIZE);
fprintf(fp, " \"%s\"", buf);
} else {
read_string(addr, buf, e->width[i]);
fprintf(fp, " %s = \"%s\"", e->member[i], buf);
}
} else if ((sym = value_search(v.v64, 0)) && is_symbol_text(sym))
fprintf(fp, " <%s>", sym->name);
fprintf(fp, "\n");
}
/*
* Does the work for cmd_list() and any other function that requires the
* contents of a linked list. See cmd_list description above for details.
*/
int
do_list(struct list_data *ld)
{
ulong next, last, first, offset;
ulong searchfor, readflag;
int i, count, others, close_hq_on_return;
unsigned int radix;
struct req_entry **e = NULL;
if (CRASHDEBUG(1)) {
others = 0;
console(" flags: %lx (", ld->flags);
if (ld->flags & VERBOSE)
console("%sVERBOSE", others++ ? "|" : "");
if (ld->flags & LIST_OFFSET_ENTERED)
console("%sLIST_OFFSET_ENTERED", others++ ? "|" : "");
if (ld->flags & LIST_START_ENTERED)
console("%sLIST_START_ENTERED", others++ ? "|" : "");
if (ld->flags & LIST_HEAD_FORMAT)
console("%sLIST_HEAD_FORMAT", others++ ? "|" : "");
if (ld->flags & LIST_HEAD_POINTER)
console("%sLIST_HEAD_POINTER", others++ ? "|" : "");
if (ld->flags & RETURN_ON_DUPLICATE)
console("%sRETURN_ON_DUPLICATE", others++ ? "|" : "");
if (ld->flags & RETURN_ON_LIST_ERROR)
console("%sRETURN_ON_LIST_ERROR", others++ ? "|" : "");
if (ld->flags & RETURN_ON_LIST_ERROR)
console("%sRETURN_ON_LIST_ERROR", others++ ? "|" : "");
if (ld->flags & LIST_STRUCT_RADIX_10)
console("%sLIST_STRUCT_RADIX_10", others++ ? "|" : "");
if (ld->flags & LIST_STRUCT_RADIX_16)
console("%sLIST_STRUCT_RADIX_16", others++ ? "|" : "");
if (ld->flags & LIST_ALLOCATE)
console("%sLIST_ALLOCATE", others++ ? "|" : "");
if (ld->flags & LIST_CALLBACK)
console("%sLIST_CALLBACK", others++ ? "|" : "");
if (ld->flags & CALLBACK_RETURN)
console("%sCALLBACK_RETURN", others++ ? "|" : "");
console(")\n");
console(" start: %lx\n", ld->start);
console(" member_offset: %ld\n", ld->member_offset);
console(" list_head_offset: %ld\n", ld->list_head_offset);
console(" end: %lx\n", ld->end);
console(" searchfor: %lx\n", ld->searchfor);
console(" structname_args: %lx\n", ld->structname_args);
if (!ld->structname_args)
console(" structname: (unused)\n");
for (i = 0; i < ld->structname_args; i++)
console(" structname[%d]: %s\n", i, ld->structname[i]);
console(" header: %s\n", ld->header);
console(" list_ptr: %lx\n", (ulong)ld->list_ptr);
console(" callback_func: %lx\n", (ulong)ld->callback_func);
console(" callback_data: %lx\n", (ulong)ld->callback_data);
console("struct_list_offset: %lx\n", ld->struct_list_offset);
}
count = 0;
searchfor = ld->searchfor;
ld->searchfor = 0;
if (ld->flags & LIST_STRUCT_RADIX_10)
radix = 10;
else if (ld->flags & LIST_STRUCT_RADIX_16)
radix = 16;
else
radix = 0;
next = ld->start;
close_hq_on_return = FALSE;
if (ld->flags & LIST_ALLOCATE) {
if (!hq_is_open()) {
hq_open();
close_hq_on_return = TRUE;
} else if (hq_is_inuse()) {
error(ld->flags & RETURN_ON_LIST_ERROR ? INFO : FATAL,
"\ndo_list: hash queue is in use?\n");
return -1;
}
}
readflag = ld->flags & RETURN_ON_LIST_ERROR ?
(RETURN_ON_ERROR|QUIET) : FAULT_ON_ERROR;
if (!readmem(next + ld->member_offset, KVADDR, &first, sizeof(void *),
"first list entry", readflag)) {
error(INFO, "\ninvalid list entry: %lx\n", next);
if (close_hq_on_return)
hq_close();
return -1;
}
if (ld->header)
fprintf(fp, "%s", ld->header);
offset = ld->list_head_offset + ld->struct_list_offset;
if (ld->structname && (ld->flags & LIST_READ_MEMBER)) {
e = (struct req_entry **)GETBUF(sizeof(*e) * ld->structname_args);
for (i = 0; i < ld->structname_args; i++)
e[i] = fill_member_offsets(ld->structname[i]);
}
while (1) {
if (ld->flags & VERBOSE) {
fprintf(fp, "%lx\n", next - ld->list_head_offset);
if (ld->structname) {
for (i = 0; i < ld->structname_args; i++) {
switch (count_chars(ld->structname[i], '.'))
{
case 0:
dump_struct(ld->structname[i],
next - offset, radix);
break;
default:
if (ld->flags & LIST_PARSE_MEMBER)
dump_struct_members(ld, i, next);
else if (ld->flags & LIST_READ_MEMBER)
dump_struct_members_fast(e[i],
radix, next - offset);
break;
}
}
}
}
if (next && !hq_enter(next - ld->list_head_offset)) {
if (ld->flags &
(RETURN_ON_DUPLICATE|RETURN_ON_LIST_ERROR)) {
error(INFO, "\nduplicate list entry: %lx\n",
next);
if (close_hq_on_return)
hq_close();
return -1;
}
error(FATAL, "\nduplicate list entry: %lx\n", next);
}
if ((searchfor == next) ||
(searchfor == (next - ld->list_head_offset)))
ld->searchfor = searchfor;
count++;
last = next;
if ((ld->flags & LIST_CALLBACK) &&
ld->callback_func((void *)(next - ld->list_head_offset),
ld->callback_data) && (ld->flags & CALLBACK_RETURN))
break;
if (!readmem(next + ld->member_offset, KVADDR, &next,
sizeof(void *), "list entry", readflag)) {
error(INFO, "\ninvalid list entry: %lx\n", next);
if (close_hq_on_return)
hq_close();
return -1;
}
if (next == 0) {
if (ld->flags & LIST_HEAD_FORMAT) {
error(INFO, "\ninvalid list entry: 0\n");
if (close_hq_on_return)
hq_close();
return -1;
}
if (CRASHDEBUG(1))
console("do_list end: next:%lx\n", next);
break;
}
if (next == ld->end) {
if (CRASHDEBUG(1))
console("do_list end: next:%lx == end:%lx\n",
next, ld->end);
break;
}
if (next == ld->start) {
if (CRASHDEBUG(1))
console("do_list end: next:%lx == start:%lx\n",
next, ld->start);
break;
}
if (next == last) {
if (CRASHDEBUG(1))
console("do_list end: next:%lx == last:%lx\n",
next, last);
break;
}
if ((next == first) && (count != 1)) {
if (CRASHDEBUG(1))
console("do_list end: next:%lx == first:%lx (count %d)\n",
next, last, count);
break;
}
}
if (CRASHDEBUG(1))
console("do_list count: %d\n", count);
if (ld->flags & LIST_ALLOCATE) {
ld->list_ptr = (ulong *)GETBUF(count * sizeof(void *));
count = retrieve_list(ld->list_ptr, count);
if (close_hq_on_return)
hq_close();
}
return count;
}
static void
do_list_debug_entry(struct list_data *ld)
{
int i, others;
if (CRASHDEBUG(1)) {
others = 0;
console(" flags: %lx (", ld->flags);
if (ld->flags & VERBOSE)
console("%sVERBOSE", others++ ? "|" : "");
if (ld->flags & LIST_OFFSET_ENTERED)
console("%sLIST_OFFSET_ENTERED", others++ ? "|" : "");
if (ld->flags & LIST_START_ENTERED)
console("%sLIST_START_ENTERED", others++ ? "|" : "");
if (ld->flags & LIST_HEAD_FORMAT)
console("%sLIST_HEAD_FORMAT", others++ ? "|" : "");
if (ld->flags & LIST_HEAD_POINTER)
console("%sLIST_HEAD_POINTER", others++ ? "|" : "");
if (ld->flags & RETURN_ON_DUPLICATE)
console("%sRETURN_ON_DUPLICATE", others++ ? "|" : "");
if (ld->flags & RETURN_ON_LIST_ERROR)
console("%sRETURN_ON_LIST_ERROR", others++ ? "|" : "");
if (ld->flags & RETURN_ON_LIST_ERROR)
console("%sRETURN_ON_LIST_ERROR", others++ ? "|" : "");
if (ld->flags & LIST_STRUCT_RADIX_10)
console("%sLIST_STRUCT_RADIX_10", others++ ? "|" : "");
if (ld->flags & LIST_STRUCT_RADIX_16)
console("%sLIST_STRUCT_RADIX_16", others++ ? "|" : "");
if (ld->flags & LIST_ALLOCATE)
console("%sLIST_ALLOCATE", others++ ? "|" : "");
if (ld->flags & LIST_CALLBACK)
console("%sLIST_CALLBACK", others++ ? "|" : "");
if (ld->flags & CALLBACK_RETURN)
console("%sCALLBACK_RETURN", others++ ? "|" : "");
console(")\n");
console(" start: %lx\n", ld->start);
console(" member_offset: %ld\n", ld->member_offset);
console(" list_head_offset: %ld\n", ld->list_head_offset);
console(" end: %lx\n", ld->end);
console(" searchfor: %lx\n", ld->searchfor);
console(" structname_args: %lx\n", ld->structname_args);
if (!ld->structname_args)
console(" structname: (unused)\n");
for (i = 0; i < ld->structname_args; i++)
console(" structname[%d]: %s\n", i, ld->structname[i]);
console(" header: %s\n", ld->header);
console(" list_ptr: %lx\n", (ulong)ld->list_ptr);
console(" callback_func: %lx\n", (ulong)ld->callback_func);
console(" callback_data: %lx\n", (ulong)ld->callback_data);
console("struct_list_offset: %lx\n", ld->struct_list_offset);
}
}
static void
do_list_output_struct(struct list_data *ld, ulong next, ulong offset,
unsigned int radix, struct req_entry **e)
{
int i;
for (i = 0; i < ld->structname_args; i++) {
switch (count_chars(ld->structname[i], '.'))
{
case 0:
dump_struct(ld->structname[i],
next - offset, radix);
break;
default:
if (ld->flags & LIST_PARSE_MEMBER)
dump_struct_members(ld, i, next);
else if (ld->flags & LIST_READ_MEMBER)
dump_struct_members_fast(e[i],
radix, next - offset);
break;
}
}
}
static int
do_list_no_hash_readmem(struct list_data *ld, ulong *next_ptr,
ulong readflag)
{
if (!readmem(*next_ptr + ld->member_offset, KVADDR, next_ptr,
sizeof(void *), "list entry", readflag)) {
error(INFO, "\ninvalid list entry: %lx\n", *next_ptr);
return -1;
}
return 0;
}
static ulong brent_x; /* tortoise */
static ulong brent_y; /* hare */
static ulong brent_r; /* power */
static ulong brent_lambda; /* loop length */
static ulong brent_mu; /* distance to start of loop */
static ulong brent_loop_detect;
static ulong brent_loop_exit;
/*
* 'ptr': representative of x or y; modified on return
*/
static int
brent_f(ulong *ptr, struct list_data *ld, ulong readflag)
{
return do_list_no_hash_readmem(ld, ptr, readflag);
}
/*
* Similar to do_list() but without the hash_table or LIST_ALLOCATE.
* Useful for the 'list' command and other callers needing faster list
* enumeration.
*/
int
do_list_no_hash(struct list_data *ld)
{
ulong next, last, first, offset;
ulong searchfor, readflag;
int i, count, ret;
unsigned int radix;
struct req_entry **e = NULL;
do_list_debug_entry(ld);
count = 0;
searchfor = ld->searchfor;
ld->searchfor = 0;
if (ld->flags & LIST_STRUCT_RADIX_10)
radix = 10;
else if (ld->flags & LIST_STRUCT_RADIX_16)
radix = 16;
else
radix = 0;
next = ld->start;
readflag = ld->flags & RETURN_ON_LIST_ERROR ?
(RETURN_ON_ERROR|QUIET) : FAULT_ON_ERROR;
if (!readmem(next + ld->member_offset, KVADDR, &first, sizeof(void *),
"first list entry", readflag)) {
error(INFO, "\ninvalid list entry: %lx\n", next);
return -1;
}
if (ld->header)
fprintf(fp, "%s", ld->header);
offset = ld->list_head_offset + ld->struct_list_offset;
if (ld->structname && (ld->flags & LIST_READ_MEMBER)) {
e = (struct req_entry **)GETBUF(sizeof(*e) * ld->structname_args);
for (i = 0; i < ld->structname_args; i++)
e[i] = fill_member_offsets(ld->structname[i]);
}
brent_loop_detect = brent_loop_exit = 0;
brent_lambda = 0;
brent_r = 2;
brent_x = brent_y = next;
ret = brent_f(&brent_y, ld, readflag);
if (ret == -1)
return -1;
while (1) {
if (!brent_loop_detect && ld->flags & VERBOSE) {
fprintf(fp, "%lx\n", next - ld->list_head_offset);
if (ld->structname) {
do_list_output_struct(ld, next, offset, radix, e);
}
}
if (next && brent_loop_exit) {
if (ld->flags &
(RETURN_ON_DUPLICATE|RETURN_ON_LIST_ERROR)) {
error(INFO, "\nduplicate list entry: %lx\n",
brent_x);
return -1;
}
error(FATAL, "\nduplicate list entry: %lx\n", brent_x);
}
if ((searchfor == next) ||
(searchfor == (next - ld->list_head_offset)))
ld->searchfor = searchfor;
count++;
last = next;
if ((ld->flags & LIST_CALLBACK) &&
ld->callback_func((void *)(next - ld->list_head_offset),
ld->callback_data) && (ld->flags & CALLBACK_RETURN))
break;
ret = do_list_no_hash_readmem(ld, &next, readflag);
if (ret == -1)
return -1;
if (!brent_loop_detect) {
if (count > 1 && brent_x == brent_y) {
brent_loop_detect = 1;
error(INFO, "loop detected, loop length: %ld\n", brent_lambda);
/* reset x and y to start; advance y loop length */
brent_mu = 0;
brent_x = brent_y = ld->start;
while (brent_lambda--) {
ret = brent_f(&brent_y, ld, readflag);
if (ret == -1)
return -1;
}
} else {
if (brent_r == brent_lambda) {
brent_x = brent_y;
brent_r *= 2;
brent_lambda = 0;
}
brent_y = next;
brent_lambda++;
}
} else {
if (!brent_loop_exit && brent_x == brent_y) {
brent_loop_exit = 1;
error(INFO, "length from start to loop: %lx",
brent_mu);
} else {
ret = brent_f(&brent_x, ld, readflag);
if (ret == -1)
return -1;
ret = brent_f(&brent_y, ld, readflag);
if (ret == -1)
return -1;
brent_mu++;
}
}
if (next == 0) {
if (ld->flags & LIST_HEAD_FORMAT) {
error(INFO, "\ninvalid list entry: 0\n");
return -1;
}
if (CRASHDEBUG(1))
console("do_list end: next:%lx\n", next);
break;
}
if (next == ld->end) {
if (CRASHDEBUG(1))
console("do_list end: next:%lx == end:%lx\n",
next, ld->end);
break;
}
if (next == ld->start) {
if (CRASHDEBUG(1))
console("do_list end: next:%lx == start:%lx\n",
next, ld->start);
break;
}
if (next == last) {
if (CRASHDEBUG(1))
console("do_list end: next:%lx == last:%lx\n",
next, last);
break;
}
if ((next == first) && (count != 1)) {
if (CRASHDEBUG(1))
console("do_list end: next:%lx == first:%lx (count %d)\n",
next, last, count);
break;
}
}
if (CRASHDEBUG(1))
console("do_list count: %d\n", count);
return count;
}
/*
* Issue a dump_struct_member() call for one or more structure
* members. Multiple members are passed in a comma-separated
* list using the the format:
*
* struct.member1,member2,member3
*/
void
dump_struct_members(struct list_data *ld, int idx, ulong next)
{
int i, argc;
char *p1, *p2;
char *structname, *members;
char *arglist[MAXARGS];
unsigned int radix;
if (ld->flags & LIST_STRUCT_RADIX_10)
radix = 10;
else if (ld->flags & LIST_STRUCT_RADIX_16)
radix = 16;
else
radix = 0;
structname = GETBUF(strlen(ld->structname[idx])+1);
members = GETBUF(strlen(ld->structname[idx])+1);
strcpy(structname, ld->structname[idx]);
p1 = strstr(structname, ".") + 1;
p2 = strstr(ld->structname[idx], ".") + 1;
strcpy(members, p2);
replace_string(members, ",", ' ');
argc = parse_line(members, arglist);
for (i = 0; i < argc; i++) {
*p1 = NULLCHAR;
strcat(structname, arglist[i]);
dump_struct_member(structname,
next - ld->list_head_offset - ld->struct_list_offset, radix);
}
FREEBUF(structname);
FREEBUF(members);
}
#define RADIXTREE_REQUEST (0x1)
#define RBTREE_REQUEST (0x2)
#define XARRAY_REQUEST (0x4)
#define MAPLE_REQUEST (0x8)
void
cmd_tree()
{
int c, type_flag, others;
long root_offset;
struct tree_data tree_data, *td;
struct datatype_member struct_member, *sm;
struct syment *sp;
ulong value;
char *type_name = NULL;
type_flag = 0;
root_offset = 0;
sm = &struct_member;
td = &tree_data;
BZERO(td, sizeof(struct tree_data));
while ((c = getopt(argcnt, args, "xdt:r:o:s:S:plNv")) != EOF) {
switch (c)
{
case 't':
if (type_flag & (RADIXTREE_REQUEST|RBTREE_REQUEST|XARRAY_REQUEST|MAPLE_REQUEST)) {
error(INFO, "multiple tree types may not be entered\n");
cmd_usage(pc->curcmd, SYNOPSIS);
}
if (STRNEQ(optarg, "ra"))
if (MEMBER_EXISTS("radix_tree_root", "xa_head")) {
type_flag = XARRAY_REQUEST;
type_name = "Xarrays";
} else {
type_flag = RADIXTREE_REQUEST;
type_name = "radix trees";
}
else if (STRNEQ(optarg, "rb")) {
type_flag = RBTREE_REQUEST;
type_name = "rbtrees";
} else if (STRNEQ(optarg, "x")) {
type_flag = XARRAY_REQUEST;
type_name = "Xarrays";
} else if (STRNEQ(optarg, "m")) {
type_flag = MAPLE_REQUEST;
type_name = "maple trees";
} else {
error(INFO, "invalid tree type: %s\n", optarg);
cmd_usage(pc->curcmd, SYNOPSIS);
}
break;
case 'l':
td->flags |= TREE_LINEAR_ORDER;
break;
case 'r':
if (td->flags & TREE_ROOT_OFFSET_ENTERED)
error(FATAL,
"root offset value %d (0x%lx) already entered\n",
root_offset, root_offset);
else if (IS_A_NUMBER(optarg))
root_offset = stol(optarg, FAULT_ON_ERROR, NULL);
else if (arg_to_datatype(optarg, sm, RETURN_ON_ERROR) > 1)
root_offset = sm->member_offset;
else
error(FATAL, "invalid -r argument: %s\n",
optarg);
td->flags |= TREE_ROOT_OFFSET_ENTERED;
break;
case 'o':
if (td->flags & TREE_NODE_OFFSET_ENTERED)
error(FATAL,
"node offset value %d (0x%lx) already entered\n",
td->node_member_offset, td->node_member_offset);
else if (IS_A_NUMBER(optarg))
td->node_member_offset = stol(optarg,
FAULT_ON_ERROR, NULL);
else if (arg_to_datatype(optarg, sm, RETURN_ON_ERROR) > 1)
td->node_member_offset = sm->member_offset;
else
error(FATAL, "invalid -o argument: %s\n",
optarg);
td->flags |= TREE_NODE_OFFSET_ENTERED;
break;
case 's':
case 'S':
if (td->structname_args++ == 0)
hq_open();
hq_enter((ulong)optarg);
td->flags |= (c == 's') ? TREE_PARSE_MEMBER : TREE_READ_MEMBER;
if (count_bits_long(td->flags & (TREE_PARSE_MEMBER|TREE_READ_MEMBER)) > 1)
error(FATAL, "-S and -s options are mutually exclusive\n");
break;
case 'p':
td->flags |= TREE_POSITION_DISPLAY;
break;
case 'N':
td->flags |= TREE_NODE_POINTER;
break;
case 'x':
if (td->flags & TREE_STRUCT_RADIX_10)
error(FATAL,
"-d and -x are mutually exclusive\n");
td->flags |= TREE_STRUCT_RADIX_16;
break;
case 'd':
if (td->flags & TREE_STRUCT_RADIX_16)
error(FATAL,
"-d and -x are mutually exclusive\n");
td->flags |= TREE_STRUCT_RADIX_10;
break;
case 'v':
td->flags |= TREE_STRUCT_VERBOSE;
break;
default:
argerrs++;
break;
}
}
if (argerrs)
cmd_usage(pc->curcmd, SYNOPSIS);
if ((type_flag & (XARRAY_REQUEST|RADIXTREE_REQUEST|MAPLE_REQUEST)) &&
(td->flags & TREE_LINEAR_ORDER))
error(FATAL, "-l option is not applicable to %s\n", type_name);
if ((type_flag & (XARRAY_REQUEST|RADIXTREE_REQUEST|MAPLE_REQUEST)) &&
(td->flags & TREE_NODE_OFFSET_ENTERED))
error(FATAL, "-o option is not applicable to %s\n", type_name);
if ((type_flag & (RBTREE_REQUEST|XARRAY_REQUEST|RADIXTREE_REQUEST)) &&
(td->flags & TREE_STRUCT_VERBOSE))
error(FATAL, "-v option is not applicable to %s\n", type_name);
if ((td->flags & TREE_ROOT_OFFSET_ENTERED) &&
(td->flags & TREE_NODE_POINTER))
error(FATAL, "-r and -N options are mutually exclusive\n");
if (!args[optind]) {
error(INFO, "a starting address is required\n");
cmd_usage(pc->curcmd, SYNOPSIS);
}
if ((sp = symbol_search(args[optind]))) {
td->start = sp->value;
goto next_arg;
}
if (!IS_A_NUMBER(args[optind])) {
if (can_eval(args[optind])) {
value = eval(args[optind], FAULT_ON_ERROR, NULL);
if (IS_KVADDR(value)) {
td->start = value;
goto next_arg;
}
}
error(FATAL, "invalid start argument: %s\n", args[optind]);
}
if (hexadecimal_only(args[optind], 0)) {
value = htol(args[optind], FAULT_ON_ERROR, NULL);
if (IS_KVADDR(value)) {
td->start = value;
goto next_arg;
}
}
error(FATAL, "invalid start argument: %s\n", args[optind]);
next_arg:
if (args[optind+1]) {
error(INFO, "too many arguments entered\n");
cmd_usage(pc->curcmd, SYNOPSIS);
}
if (td->structname_args) {
td->structname = (char **)GETBUF(sizeof(char *) *
td->structname_args);
retrieve_list((ulong *)td->structname, td->structname_args);
hq_close();
}
if (!(td->flags & TREE_NODE_POINTER))
td->start = td->start + root_offset;
if (CRASHDEBUG(1)) {
others = 0;
fprintf(fp, " flags: %lx (", td->flags);
if (td->flags & TREE_ROOT_OFFSET_ENTERED)
fprintf(fp, "%sTREE_ROOT_OFFSET_ENTERED",
others++ ? "|" : "");
if (td->flags & TREE_NODE_OFFSET_ENTERED)
fprintf(fp, "%sTREE_NODE_OFFSET_ENTERED",
others++ ? "|" : "");
if (td->flags & TREE_NODE_POINTER)
fprintf(fp, "%sTREE_NODE_POINTER",
others++ ? "|" : "");
if (td->flags & TREE_POSITION_DISPLAY)
fprintf(fp, "%sTREE_POSITION_DISPLAY",
others++ ? "|" : "");
if (td->flags & TREE_STRUCT_RADIX_10)
fprintf(fp, "%sTREE_STRUCT_RADIX_10",
others++ ? "|" : "");
if (td->flags & TREE_STRUCT_RADIX_16)
fprintf(fp, "%sTREE_STRUCT_RADIX_16",
others++ ? "|" : "");
if (td->flags & TREE_PARSE_MEMBER)
fprintf(fp, "%sTREE_PARSE_MEMBER",
others++ ? "|" : "");
if (td->flags & TREE_READ_MEMBER)
fprintf(fp, "%sTREE_READ_MEMBER",
others++ ? "|" : "");
if (td->flags & TREE_LINEAR_ORDER)
fprintf(fp, "%sTREE_LINEAR_ORDER",
others++ ? "|" : "");
if (td->flags & TREE_STRUCT_VERBOSE)
fprintf(fp, "%sTREE_STRUCT_VERBOSE",
others++ ? "|" : "");
fprintf(fp, ")\n");
fprintf(fp, " type: ");
if (type_flag & RADIXTREE_REQUEST)
fprintf(fp, "radix\n");
else if (type_flag & XARRAY_REQUEST)
fprintf(fp, "xarray\n");
else if (type_flag & MAPLE_REQUEST)
fprintf(fp, "maple\n");
else
fprintf(fp, "red-black%s",
type_flag & RBTREE_REQUEST ?
"\n" : " (default)\n");
fprintf(fp, " node pointer: %s\n",
td->flags & TREE_NODE_POINTER ? "yes" : "no");
fprintf(fp, " start: %lx\n", td->start);
fprintf(fp, "node_member_offset: %ld\n", td->node_member_offset);
fprintf(fp, " structname_args: %d\n", td->structname_args);
fprintf(fp, " count: %d\n", td->count);
}
td->flags &= ~TREE_NODE_OFFSET_ENTERED;
td->flags |= VERBOSE;
hq_open();
if (type_flag & RADIXTREE_REQUEST)
do_rdtree(td);
else if (type_flag & XARRAY_REQUEST)
do_xatree(td);
else if (type_flag & MAPLE_REQUEST)
do_mptree(td);
else
do_rbtree(td);
hq_close();
if (td->structname_args)
FREEBUF(td->structname);
}
static ulong RADIX_TREE_MAP_SHIFT = UNINITIALIZED;
static ulong RADIX_TREE_MAP_SIZE = UNINITIALIZED;
static ulong RADIX_TREE_MAP_MASK = UNINITIALIZED;
#define RADIX_TREE_ENTRY_MASK 3UL
#define RADIX_TREE_INTERNAL_NODE 1UL
static void do_radix_tree_iter(ulong node, uint height, char *path,
ulong index, struct radix_tree_ops *ops)
{
uint off;
if (!hq_enter(node))
error(FATAL,
"\nduplicate tree node: %lx\n", node);
for (off = 0; off < RADIX_TREE_MAP_SIZE; off++) {
ulong slot;
ulong shift = (height - 1) * RADIX_TREE_MAP_SHIFT;
readmem(node + OFFSET(radix_tree_node_slots) +
sizeof(void *) * off, KVADDR, &slot, sizeof(void *),
"radix_tree_node.slot[off]", FAULT_ON_ERROR);
if (!slot)
continue;
if (slot & RADIX_TREE_INTERNAL_NODE)
slot &= ~RADIX_TREE_INTERNAL_NODE;
if (height == 1)
ops->entry(node, slot, path, index | off, ops->private);
else {
ulong child_index = index | (off << shift);
char child_path[BUFSIZE];
sprintf(child_path, "%s/%d", path, off);
do_radix_tree_iter(slot, height - 1,
child_path, child_index, ops);
}
}
}
int do_radix_tree_traverse(ulong ptr, int is_root, struct radix_tree_ops *ops)
{
static ulong max_height = UNINITIALIZED;
ulong node_p;
long nlen;
uint height, is_internal;
unsigned char shift;
char path[BUFSIZE];
if (!VALID_STRUCT(radix_tree_root) || !VALID_STRUCT(radix_tree_node) ||
((!VALID_MEMBER(radix_tree_root_height) ||
!VALID_MEMBER(radix_tree_root_rnode) ||
!VALID_MEMBER(radix_tree_node_slots) ||
!ARRAY_LENGTH(height_to_maxindex)) &&
(!VALID_MEMBER(radix_tree_root_rnode) ||
!VALID_MEMBER(radix_tree_node_shift) ||
!VALID_MEMBER(radix_tree_node_slots) ||
!ARRAY_LENGTH(height_to_maxnodes))))
error(FATAL, "radix trees do not exist or have changed "
"their format\n");
if (RADIX_TREE_MAP_SHIFT == UNINITIALIZED) {
if (!(nlen = MEMBER_SIZE("radix_tree_node", "slots")))
error(FATAL, "cannot determine length of "
"radix_tree_node.slots[] array\n");
nlen /= sizeof(void *);
RADIX_TREE_MAP_SHIFT = ffsl(nlen) - 1;
RADIX_TREE_MAP_SIZE = (1UL << RADIX_TREE_MAP_SHIFT);
RADIX_TREE_MAP_MASK = (RADIX_TREE_MAP_SIZE-1);
if (ARRAY_LENGTH(height_to_maxindex))
max_height = ARRAY_LENGTH(height_to_maxindex);
else
max_height = ARRAY_LENGTH(height_to_maxnodes);
}
height = 0;
if (!is_root) {
node_p = ptr;
if (node_p & RADIX_TREE_INTERNAL_NODE)
node_p &= ~RADIX_TREE_INTERNAL_NODE;
if (VALID_MEMBER(radix_tree_node_height)) {
readmem(node_p + OFFSET(radix_tree_node_height), KVADDR,
&height, sizeof(uint), "radix_tree_node height",
FAULT_ON_ERROR);
} else if (VALID_MEMBER(radix_tree_node_shift)) {
readmem(node_p + OFFSET(radix_tree_node_shift), KVADDR,
&shift, sizeof(shift), "radix_tree_node shift",
FAULT_ON_ERROR);
height = (shift / RADIX_TREE_MAP_SHIFT) + 1;
} else
error(FATAL, "-N option is not supported or applicable"
" for radix trees on this architecture or kernel\n");
if (height > max_height)
goto error_height;
} else {
if (VALID_MEMBER(radix_tree_root_height)) {
readmem(ptr + OFFSET(radix_tree_root_height), KVADDR, &height,
sizeof(uint), "radix_tree_root height", FAULT_ON_ERROR);
}
readmem(ptr + OFFSET(radix_tree_root_rnode), KVADDR, &node_p,
sizeof(void *), "radix_tree_root rnode", FAULT_ON_ERROR);
is_internal = (node_p & RADIX_TREE_INTERNAL_NODE);
if (node_p & RADIX_TREE_INTERNAL_NODE)
node_p &= ~RADIX_TREE_INTERNAL_NODE;
if (is_internal && VALID_MEMBER(radix_tree_node_shift)) {
readmem(node_p + OFFSET(radix_tree_node_shift), KVADDR, &shift,
sizeof(shift), "radix_tree_node shift", FAULT_ON_ERROR);
height = (shift / RADIX_TREE_MAP_SHIFT) + 1;
}
if (height > max_height) {
node_p = ptr;
goto error_height;
}
}
if (CRASHDEBUG(1)) {
fprintf(fp, "radix_tree_node.slots[%ld]\n",
RADIX_TREE_MAP_SIZE);
fprintf(fp, "max_height %ld: ", max_height);
fprintf(fp, "\n");
fprintf(fp, "pointer at %lx (is_root? %s):\n",
node_p, is_root ? "yes" : "no");
if (is_root)
dump_struct("radix_tree_root", ptr, RADIX(ops->radix));
else
dump_struct("radix_tree_node", node_p, RADIX(ops->radix));
}
if (height == 0) {
strcpy(path, "direct");
ops->entry(node_p, node_p, path, 0, ops->private);
} else {
strcpy(path, "root");
do_radix_tree_iter(node_p, height, path, 0, ops);
}
return 0;
error_height:
fprintf(fp, "radix_tree_node at %lx\n", node_p);
dump_struct("radix_tree_node", node_p, RADIX(ops->radix));
error(FATAL, "height %d is greater than "
"maximum radix tree height index %ld\n",
height, max_height);
return -1;
}
static ulong XA_CHUNK_SHIFT = UNINITIALIZED;
static ulong XA_CHUNK_SIZE = UNINITIALIZED;
static ulong XA_CHUNK_MASK = UNINITIALIZED;
static void
do_xarray_iter(ulong node, uint height, char *path,
ulong index, struct xarray_ops *ops)
{
uint off;
if (!hq_enter(node))
error(FATAL,
"\nduplicate tree node: %lx\n", node);
for (off = 0; off < XA_CHUNK_SIZE; off++) {
ulong slot;
ulong shift = (height - 1) * XA_CHUNK_SHIFT;
readmem(node + OFFSET(xa_node_slots) +
sizeof(void *) * off, KVADDR, &slot, sizeof(void *),
"xa_node.slots[off]", FAULT_ON_ERROR);
if (!slot)
continue;
if ((slot & XARRAY_TAG_MASK) == XARRAY_TAG_INTERNAL)
slot &= ~XARRAY_TAG_INTERNAL;
if (height == 1)
ops->entry(node, slot, path, index | off, ops->private);
else {
ulong child_index = index | (off << shift);
char child_path[BUFSIZE];
sprintf(child_path, "%s/%d", path, off);
do_xarray_iter(slot, height - 1,
child_path, child_index, ops);
}
}
}
int
do_xarray_traverse(ulong ptr, int is_root, struct xarray_ops *ops)
{
ulong node_p;
long nlen;
uint height, is_internal;
unsigned char shift;
char path[BUFSIZE];
if (!VALID_STRUCT(xarray) || !VALID_STRUCT(xa_node) ||
!VALID_MEMBER(xarray_xa_head) ||
!VALID_MEMBER(xa_node_slots) ||
!VALID_MEMBER(xa_node_shift))
error(FATAL,
"xarray facility does not exist or has changed its format\n");
if (XA_CHUNK_SHIFT == UNINITIALIZED) {
if ((nlen = MEMBER_SIZE("xa_node", "slots")) <= 0)
error(FATAL, "cannot determine length of xa_node.slots[] array\n");
nlen /= sizeof(void *);
XA_CHUNK_SHIFT = ffsl(nlen) - 1;
XA_CHUNK_SIZE = (1UL << XA_CHUNK_SHIFT);
XA_CHUNK_MASK = (XA_CHUNK_SIZE-1);
}
height = 0;
if (!is_root) {
node_p = ptr;
if ((node_p & XARRAY_TAG_MASK) == XARRAY_TAG_INTERNAL)
node_p &= ~XARRAY_TAG_MASK;
if (VALID_MEMBER(xa_node_shift)) {
readmem(node_p + OFFSET(xa_node_shift), KVADDR,
&shift, sizeof(shift), "xa_node shift",
FAULT_ON_ERROR);
height = (shift / XA_CHUNK_SHIFT) + 1;
} else
error(FATAL, "-N option is not supported or applicable"
" for xarrays on this architecture or kernel\n");
} else {
readmem(ptr + OFFSET(xarray_xa_head), KVADDR, &node_p,
sizeof(void *), "xarray xa_head", FAULT_ON_ERROR);
is_internal = ((node_p & XARRAY_TAG_MASK) == XARRAY_TAG_INTERNAL);
if (node_p & XARRAY_TAG_MASK)
node_p &= ~XARRAY_TAG_MASK;
if (is_internal && VALID_MEMBER(xa_node_shift)) {
readmem(node_p + OFFSET(xa_node_shift), KVADDR, &shift,
sizeof(shift), "xa_node shift", FAULT_ON_ERROR);
height = (shift / XA_CHUNK_SHIFT) + 1;
}
}
if (CRASHDEBUG(1)) {
fprintf(fp, "xa_node.slots[%ld]\n", XA_CHUNK_SIZE);
fprintf(fp, "pointer at %lx (is_root? %s):\n",
node_p, is_root ? "yes" : "no");
if (is_root)
dump_struct("xarray", ptr, RADIX(ops->radix));
else
dump_struct("xa_node", node_p, RADIX(ops->radix));
}
if (height == 0) {
strcpy(path, "direct");
ops->entry(node_p, node_p, path, 0, ops->private);
} else {
strcpy(path, "root");
do_xarray_iter(node_p, height, path, 0, ops);
}
return 0;
}
static void do_rdtree_entry(ulong node, ulong slot, const char *path,
ulong index, void *private)
{
struct tree_data *td = private;
static struct req_entry **e = NULL;
uint print_radix;
int i;
if (!td->count && td->structname_args) {
/*
* Retrieve all members' info only once (count == 0)
* After last iteration all memory will be freed up
*/
e = (struct req_entry **)GETBUF(sizeof(*e) * td->structname_args);
for (i = 0; i < td->structname_args; i++)
e[i] = fill_member_offsets(td->structname[i]);
}
td->count++;
if (td->flags & VERBOSE)
fprintf(fp, "%lx\n", slot);
if (td->flags & TREE_POSITION_DISPLAY) {
fprintf(fp, " index: %ld position: %s/%ld\n", index,
path, index & RADIX_TREE_MAP_MASK);
}
if (td->structname) {
if (td->flags & TREE_STRUCT_RADIX_10)
print_radix = 10;
else if (td->flags & TREE_STRUCT_RADIX_16)
print_radix = 16;
else
print_radix = 0;
for (i = 0; i < td->structname_args; i++) {
switch (count_chars(td->structname[i], '.')) {
case 0:
dump_struct(td->structname[i], slot, print_radix);
break;
default:
if (td->flags & TREE_PARSE_MEMBER)
dump_struct_members_for_tree(td, i, slot);
else if (td->flags & TREE_READ_MEMBER)
dump_struct_members_fast(e[i], print_radix, slot);
break;
}
}
}
}
int do_rdtree(struct tree_data *td)
{
struct radix_tree_ops ops = {
.entry = do_rdtree_entry,
.private = td,
};
int is_root = !(td->flags & TREE_NODE_POINTER);
if (td->flags & TREE_STRUCT_RADIX_10)
ops.radix = 10;
else if (td->flags & TREE_STRUCT_RADIX_16)
ops.radix = 16;
else
ops.radix = 0;
do_radix_tree_traverse(td->start, is_root, &ops);
return 0;
}
static void do_xarray_entry(ulong node, ulong slot, const char *path,
ulong index, void *private)
{
struct tree_data *td = private;
static struct req_entry **e = NULL;
uint print_radix;
int i;
if (!td->count && td->structname_args) {
/*
* Retrieve all members' info only once (count == 0)
* After last iteration all memory will be freed up
*/
e = (struct req_entry **)GETBUF(sizeof(*e) * td->structname_args);
for (i = 0; i < td->structname_args; i++)
e[i] = fill_member_offsets(td->structname[i]);
}
td->count++;
if (td->flags & VERBOSE)
fprintf(fp, "%lx\n", slot);
if (td->flags & TREE_POSITION_DISPLAY) {
fprintf(fp, " index: %ld position: %s/%ld\n", index,
path, index & XA_CHUNK_MASK);
}
if (td->structname) {
if (td->flags & TREE_STRUCT_RADIX_10)
print_radix = 10;
else if (td->flags & TREE_STRUCT_RADIX_16)
print_radix = 16;
else
print_radix = 0;
for (i = 0; i < td->structname_args; i++) {
switch (count_chars(td->structname[i], '.')) {
case 0:
dump_struct(td->structname[i], slot, print_radix);
break;
default:
if (td->flags & TREE_PARSE_MEMBER)
dump_struct_members_for_tree(td, i, slot);
else if (td->flags & TREE_READ_MEMBER)
dump_struct_members_fast(e[i], print_radix, slot);
break;
}
}
}
}
int do_xatree(struct tree_data *td)
{
struct xarray_ops ops = {
.entry = do_xarray_entry,
.private = td,
};
int is_root = !(td->flags & TREE_NODE_POINTER);
if (td->flags & TREE_STRUCT_RADIX_10)
ops.radix = 10;
else if (td->flags & TREE_STRUCT_RADIX_16)
ops.radix = 16;
else
ops.radix = 0;
do_xarray_traverse(td->start, is_root, &ops);
return 0;
}
int
do_rbtree(struct tree_data *td)
{
ulong start;
char pos[BUFSIZE];
if (!VALID_MEMBER(rb_root_rb_node) || !VALID_MEMBER(rb_node_rb_left) ||
!VALID_MEMBER(rb_node_rb_right))
error(FATAL, "red-black trees do not exist or have changed "
"their format\n");
sprintf(pos, "root");
if (td->flags & TREE_NODE_POINTER)
start = td->start;
else
readmem(td->start + OFFSET(rb_root_rb_node), KVADDR,
&start, sizeof(void *), "rb_root rb_node", FAULT_ON_ERROR);
rbtree_iteration(start, td, pos);
return td->count;
}
void
rbtree_iteration(ulong node_p, struct tree_data *td, char *pos)
{
int i;
uint print_radix;
ulong struct_p, new_p, test_p;
char new_pos[BUFSIZE];
static struct req_entry **e;
if (!node_p)
return;
if (!td->count && td->structname_args) {
/*
* Retrieve all members' info only once (count == 0)
* After last iteration all memory will be freed up
*/
e = (struct req_entry **)GETBUF(sizeof(*e) *
td->structname_args);
for (i = 0; i < td->structname_args; i++)
e[i] = fill_member_offsets(td->structname[i]);
}
if (hq_enter(node_p))
td->count++;
else
error(FATAL, "\nduplicate tree entry: %lx\n", node_p);
if ((td->flags & TREE_LINEAR_ORDER) &&
readmem(node_p+OFFSET(rb_node_rb_left), KVADDR, &new_p,
sizeof(void *), "rb_node rb_left", RETURN_ON_ERROR) && new_p) {
if (readmem(new_p+OFFSET(rb_node_rb_left), KVADDR, &test_p,
sizeof(void *), "rb_node rb_left", RETURN_ON_ERROR|QUIET)) {
sprintf(new_pos, "%s/l", pos);
rbtree_iteration(new_p, td, new_pos);
} else
error(INFO, "rb_node: %lx: corrupted rb_left pointer: %lx\n",
node_p, new_p);
}
struct_p = node_p - td->node_member_offset;
if (td->flags & VERBOSE)
fprintf(fp, "%lx\n", struct_p);
if (td->flags & TREE_POSITION_DISPLAY)
fprintf(fp, " position: %s\n", pos);
if (td->structname) {
if (td->flags & TREE_STRUCT_RADIX_10)
print_radix = 10;
else if (td->flags & TREE_STRUCT_RADIX_16)
print_radix = 16;
else
print_radix = 0;
for (i = 0; i < td->structname_args; i++) {
switch(count_chars(td->structname[i], '.'))
{
case 0:
dump_struct(td->structname[i], struct_p, print_radix);
break;
default:
if (td->flags & TREE_PARSE_MEMBER)
dump_struct_members_for_tree(td, i, struct_p);
else if (td->flags & TREE_READ_MEMBER)
dump_struct_members_fast(e[i], print_radix,
struct_p);
break;
}
}
}
if (!(td->flags & TREE_LINEAR_ORDER) &&
readmem(node_p+OFFSET(rb_node_rb_left), KVADDR, &new_p,
sizeof(void *), "rb_node rb_left", RETURN_ON_ERROR) && new_p) {
if (readmem(new_p+OFFSET(rb_node_rb_left), KVADDR, &test_p,
sizeof(void *), "rb_node rb_left", RETURN_ON_ERROR|QUIET)) {
sprintf(new_pos, "%s/l", pos);
rbtree_iteration(new_p, td, new_pos);
} else
error(INFO, "rb_node: %lx: corrupted rb_left pointer: %lx\n",
node_p, new_p);
}
if (readmem(node_p+OFFSET(rb_node_rb_right), KVADDR, &new_p,
sizeof(void *), "rb_node rb_right", RETURN_ON_ERROR) && new_p) {
if (readmem(new_p+OFFSET(rb_node_rb_left), KVADDR, &test_p,
sizeof(void *), "rb_node rb_left", RETURN_ON_ERROR|QUIET)) {
sprintf(new_pos, "%s/r", pos);
rbtree_iteration(new_p, td, new_pos);
} else
error(INFO, "rb_node: %lx: corrupted rb_right pointer: %lx\n",
node_p, new_p);
}
}
void
dump_struct_members_for_tree(struct tree_data *td, int idx, ulong struct_p)
{
int i, argc;
uint print_radix;
char *p1;
char *structname, *members;
char *arglist[MAXARGS];
if (td->flags & TREE_STRUCT_RADIX_10)
print_radix = 10;
else if (td->flags & TREE_STRUCT_RADIX_16)
print_radix = 16;
else
print_radix = 0;
structname = GETBUF(strlen(td->structname[idx])+1);
members = GETBUF(strlen(td->structname[idx])+1);
strcpy(structname, td->structname[idx]);
p1 = strstr(structname, ".") + 1;
strcpy(members, p1);
replace_string(members, ",", ' ');
argc = parse_line(members, arglist);
for (i = 0; i <argc; i++) {
*p1 = NULLCHAR;
strcat(structname, arglist[i]);
dump_struct_member(structname, struct_p, print_radix);
}
FREEBUF(structname);
FREEBUF(members);
}
/*
* The next set of functions are a general purpose hashing tool used to
* identify duplicate entries in a set of passed-in data, and if found,
* to fail the entry attempt. When a command wishes to verify a list
* of contains unique values, the hash functions should be used in the
* following order:
*
* hq_open()
* hq_enter(value_1)
* hq_enter(value_2)
* ...
* hq_enter(value_n)
* hq_close()
*
* If a duplicate entry is passed in between the hq_open()/hq_close() pair,
* hq_enter() will return FALSE;
*/
#define HASH_QUEUE_NONE (0x1)
#define HASH_QUEUE_FULL (0x2)
#define HASH_QUEUE_OPEN (0x4)
#define HASH_QUEUE_CLOSED (0x8)
#define HQ_ENTRY_CHUNK (1024)
#define NR_HASH_QUEUES_DEFAULT (32768UL)
#define HQ_SHIFT (machdep->pageshift)
#define HQ_INDEX(X) (((X) >> HQ_SHIFT) % pc->nr_hash_queues)
struct hq_entry {
int next;
int order;
ulong value;
};
struct hq_head {
int next;
int qcnt;
};
struct hash_table {
ulong flags;
struct hq_head *queue_heads;
struct hq_entry *memptr;
long count;
long index;
int reallocs;
} hash_table = { 0 };
/*
* For starters, allocate a hash table containing HQ_ENTRY_CHUNK entries.
* If necessary during runtime, it will be increased in size.
*/
void
hq_init(void)
{
struct hash_table *ht;
ht = &hash_table;
if (pc->nr_hash_queues == 0)
pc->nr_hash_queues = NR_HASH_QUEUES_DEFAULT;
if ((ht->queue_heads = (struct hq_head *)malloc(pc->nr_hash_queues *
sizeof(struct hq_head))) == NULL) {
error(INFO, "cannot malloc memory for hash queue heads: %s\n",
strerror(errno));
ht->flags = HASH_QUEUE_NONE;
pc->flags &= ~HASH;
return;
}
if ((ht->memptr = (struct hq_entry *)malloc(HQ_ENTRY_CHUNK *
sizeof(struct hq_entry))) == NULL) {
error(INFO, "cannot malloc memory for hash queues: %s\n",
strerror(errno));
ht->flags = HASH_QUEUE_NONE;
pc->flags &= ~HASH;
return;
}
BZERO(ht->memptr, HQ_ENTRY_CHUNK * sizeof(struct hq_entry));
ht->count = HQ_ENTRY_CHUNK;
ht->index = 0;
}
/*
* Get a free hash queue entry. If there's no more available, realloc()
* a new chunk of memory with another HQ_ENTRY_CHUNK entries stuck on the end.
*/
static long
alloc_hq_entry(void)
{
struct hash_table *ht;
struct hq_entry *new, *end_of_old;
ht = &hash_table;
if (++ht->index == ht->count) {
if (!(new = (void *)realloc((void *)ht->memptr,
(ht->count+HQ_ENTRY_CHUNK) * sizeof(struct hq_entry)))) {
error(INFO,
"cannot realloc memory for hash queues: %s\n",
strerror(errno));
ht->flags |= HASH_QUEUE_FULL;
return(-1);
}
ht->reallocs++;
ht->memptr = new;
end_of_old = ht->memptr + ht->count;
BZERO(end_of_old, HQ_ENTRY_CHUNK * sizeof(struct hq_entry));
ht->count += HQ_ENTRY_CHUNK;
}
return(ht->index);
}
/*
* Restore the hash queue to its state before the duplicate entry
* was attempted.
*/
static void
dealloc_hq_entry(struct hq_entry *entry)
{
struct hash_table *ht;
long hqi;
ht = &hash_table;
hqi = HQ_INDEX(entry->value);
ht->index--;
BZERO(entry, sizeof(struct hq_entry));
ht->queue_heads[hqi].qcnt--;
}
/*
* Initialize the hash table for a hashing session.
*/
int
hq_open(void)
{
struct hash_table *ht;
if (!(pc->flags & HASH))
return FALSE;
ht = &hash_table;
if (ht->flags & (HASH_QUEUE_NONE|HASH_QUEUE_OPEN))
return FALSE;
ht->flags &= ~(HASH_QUEUE_FULL|HASH_QUEUE_CLOSED);
BZERO(ht->queue_heads, sizeof(struct hq_head) * pc->nr_hash_queues);
BZERO(ht->memptr, ht->count * sizeof(struct hq_entry));
ht->index = 0;
ht->flags |= HASH_QUEUE_OPEN;
return TRUE;
}
int
hq_is_open(void)
{
struct hash_table *ht;
ht = &hash_table;
return (ht->flags & HASH_QUEUE_OPEN ? TRUE : FALSE);
}
int
hq_is_inuse(void)
{
struct hash_table *ht;
if (!hq_is_open())
return FALSE;
ht = &hash_table;
return (ht->index ? TRUE : FALSE);
}
/*
* Close the hash table, returning the number of items hashed in this session.
*/
int
hq_close(void)
{
struct hash_table *ht;
ht = &hash_table;
ht->flags &= ~(HASH_QUEUE_OPEN);
ht->flags |= HASH_QUEUE_CLOSED;
if (!(pc->flags & HASH))
return(0);
if (ht->flags & HASH_QUEUE_NONE)
return(0);
ht->flags &= ~HASH_QUEUE_FULL;
return(ht->index);
}
char *corrupt_hq = "corrupt hash queue entry: value: %lx next: %d order: %d\n";
/*
* For a given value, allocate a hash queue entry and hash it into the
* open hash table. If a duplicate entry is found, return FALSE; for all
* other possibilities return TRUE. Note that it's up to the user to deal
* with failure.
*/
int
hq_enter(ulong value)
{
struct hash_table *ht;
struct hq_entry *entry;
struct hq_entry *list_entry;
long hqi;
long index;
if (!(pc->flags & HASH))
return TRUE;
ht = &hash_table;
if (ht->flags & (HASH_QUEUE_NONE|HASH_QUEUE_FULL))
return TRUE;
if (!(ht->flags & HASH_QUEUE_OPEN))
return TRUE;
if ((index = alloc_hq_entry()) < 0)
return TRUE;
entry = ht->memptr + index;
if (entry->next || entry->value || entry->order) {
error(INFO, corrupt_hq,
entry->value, entry->next, entry->order);
ht->flags |= HASH_QUEUE_NONE;
return TRUE;
}
entry->next = 0;
entry->value = value;
entry->order = index;
hqi = HQ_INDEX(value);
if (ht->queue_heads[hqi].next == 0) {
ht->queue_heads[hqi].next = index;
ht->queue_heads[hqi].qcnt = 1;
return TRUE;
} else
ht->queue_heads[hqi].qcnt++;
list_entry = ht->memptr + ht->queue_heads[hqi].next;
while (TRUE) {
if (list_entry->value == entry->value) {
dealloc_hq_entry(entry);
return FALSE;
}
if (list_entry->next >= ht->count) {
error(INFO, corrupt_hq,
list_entry->value,
list_entry->next,
list_entry->order);
ht->flags |= HASH_QUEUE_NONE;
return TRUE;
}
if (list_entry->next == 0)
break;
list_entry = ht->memptr + list_entry->next;
}
list_entry->next = index;
return TRUE;
}
/*
* "hash -d" output
*/
void
dump_hash_table(int verbose)
{
int i;
struct hash_table *ht;
struct hq_entry *list_entry;
long elements;
long queues_in_use;
int others;
uint minq, maxq;
ht = &hash_table;
others = 0;
fprintf(fp, " flags: %lx (", ht->flags);
if (ht->flags & HASH_QUEUE_NONE)
fprintf(fp, "%sHASH_QUEUE_NONE", others++ ? "|" : "");
if (ht->flags & HASH_QUEUE_OPEN)
fprintf(fp, "%sHASH_QUEUE_OPEN", others++ ? "|" : "");
if (ht->flags & HASH_QUEUE_CLOSED)
fprintf(fp, "%sHASH_QUEUE_CLOSED", others++ ? "|" : "");
if (ht->flags & HASH_QUEUE_FULL)
fprintf(fp, "%sHASH_QUEUE_FULL", others++ ? "|" : "");
fprintf(fp, ")\n");
fprintf(fp, " queue_heads[%ld]: %lx\n", pc->nr_hash_queues,
(ulong)ht->queue_heads);
fprintf(fp, " memptr: %lx\n", (ulong)ht->memptr);
fprintf(fp, " count: %ld ", ht->count);
if (ht->reallocs)
fprintf(fp, " (%d reallocs)", ht->reallocs);
fprintf(fp, "\n");
fprintf(fp, " index: %ld\n", ht->index);
queues_in_use = 0;
minq = ~(0);
maxq = 0;
for (i = 0; i < pc->nr_hash_queues; i++) {
if (ht->queue_heads[i].next == 0) {
minq = 0;
continue;
}
if (ht->queue_heads[i].qcnt < minq)
minq = ht->queue_heads[i].qcnt;
if (ht->queue_heads[i].qcnt > maxq)
maxq = ht->queue_heads[i].qcnt;
queues_in_use++;
}
elements = 0;
list_entry = ht->memptr;
for (i = 0; i < ht->count; i++, list_entry++) {
if (!list_entry->order) {
if (list_entry->value || list_entry->next)
goto corrupt_list_entry;
continue;
}
if (list_entry->next >= ht->count)
goto corrupt_list_entry;
++elements;
}
if (elements != ht->index)
fprintf(fp, " elements found: %ld (expected %ld)\n",
elements, ht->index);
fprintf(fp, " queues in use: %ld of %ld\n", queues_in_use,
pc->nr_hash_queues);
fprintf(fp, " queue length range: %d to %d\n", minq, maxq);
if (verbose) {
if (!elements) {
fprintf(fp, " entries: (none)\n");
return;
}
fprintf(fp, " entries: ");
list_entry = ht->memptr;
for (i = 0; i < ht->count; i++, list_entry++) {
if (list_entry->order)
fprintf(fp, "%s%lx (%d)\n",
list_entry->order == 1 ?
"" : " ",
list_entry->value, list_entry->order);
}
}
return;
corrupt_list_entry:
error(INFO, corrupt_hq,
list_entry->value, list_entry->next, list_entry->order);
ht->flags |= HASH_QUEUE_NONE;
}
/*
* Retrieve the count of, and optionally stuff a pre-allocated array with,
* the current hash table entries. The entries will be sorted according
* to the order in which they were entered, so from this point on, no
* further hq_enter() operations on this list will be allowed. However,
* multiple calls to retrieve_list are allowed because the second and
* subsequent ones will go directly to where the non-zero (valid) entries
* start in the potentially very large list_entry memory chunk.
*/
int
retrieve_list(ulong array[], int count)
{
int i;
struct hash_table *ht;
struct hq_entry *list_entry;
int elements;
if (!(pc->flags & HASH))
error(FATAL,
"cannot perform this command with hash turned off\n");
ht = &hash_table;
list_entry = ht->memptr;
for (i = elements = 0; i < ht->count; i++, list_entry++) {
if (!list_entry->order) {
if (list_entry->value || list_entry->next)
goto corrupt_list_entry;
continue;
}
if (list_entry->next >= ht->count)
goto corrupt_list_entry;
if (array)
array[elements] = list_entry->value;
if (++elements == count)
break;
}
return elements;
corrupt_list_entry:
error(INFO, corrupt_hq,
list_entry->value, list_entry->next, list_entry->order);
ht->flags |= HASH_QUEUE_NONE;
return(-1);
}
/*
* For a given value, check to see if a hash queue entry exists. If an
* entry is found, return TRUE; for all other possibilities return FALSE.
*/
int
hq_entry_exists(ulong value)
{
struct hash_table *ht;
struct hq_entry *list_entry;
long hqi;
if (!(pc->flags & HASH))
return FALSE;
ht = &hash_table;
if (ht->flags & (HASH_QUEUE_NONE))
return FALSE;
if (!(ht->flags & HASH_QUEUE_OPEN))
return FALSE;
hqi = HQ_INDEX(value);
list_entry = ht->memptr + ht->queue_heads[hqi].next;
while (TRUE) {
if (list_entry->value == value)
return TRUE;
if (list_entry->next >= ht->count) {
error(INFO, corrupt_hq,
list_entry->value,
list_entry->next,
list_entry->order);
ht->flags |= HASH_QUEUE_NONE;
return FALSE;
}
if (list_entry->next == 0)
break;
list_entry = ht->memptr + list_entry->next;
}
return FALSE;
}
/*
* K&R power function for integers
*/
long
power(long base, int exp)
{
int i;
long p;
p = 1;
for (i = 1; i <= exp; i++)
p = p * base;
return p;
}
long long
ll_power(long long base, long long exp)
{
long long i;
long long p;
p = 1;
for (i = 1; i <= exp; i++)
p = p * base;
return p;
}
/*
* Internal buffer allocation scheme to avoid inline malloc() calls and
* resultant memory leaks due to aborted commands. These buffers are
* for TEMPORARY use on a per-command basis. They are allocated by calls
* to GETBUF(size). They can explicitly freed by FREEBUF(address), but
* they are all freed by free_all_bufs() which is called in a number of
* places, most not
*/
#define NUMBER_1K_BUFS (10)
#define NUMBER_2K_BUFS (10)
#define NUMBER_4K_BUFS (5)
#define NUMBER_8K_BUFS (5)
#define NUMBER_32K_BUFS (1)
#define SHARED_1K_BUF_FULL (0x003ff)
#define SHARED_2K_BUF_FULL (0x003ff)
#define SHARED_4K_BUF_FULL (0x0001f)
#define SHARED_8K_BUF_FULL (0x0001f)
#define SHARED_32K_BUF_FULL (0x00001)
#define SHARED_1K_BUF_AVAIL(X) \
(NUMBER_1K_BUFS && !(((X) & SHARED_1K_BUF_FULL) == SHARED_1K_BUF_FULL))
#define SHARED_2K_BUF_AVAIL(X) \
(NUMBER_2K_BUFS && !(((X) & SHARED_2K_BUF_FULL) == SHARED_2K_BUF_FULL))
#define SHARED_4K_BUF_AVAIL(X) \
(NUMBER_4K_BUFS && !(((X) & SHARED_4K_BUF_FULL) == SHARED_4K_BUF_FULL))
#define SHARED_8K_BUF_AVAIL(X) \
(NUMBER_8K_BUFS && !(((X) & SHARED_8K_BUF_FULL) == SHARED_8K_BUF_FULL))
#define SHARED_32K_BUF_AVAIL(X) \
(NUMBER_32K_BUFS && !(((X) & SHARED_32K_BUF_FULL) == SHARED_32K_BUF_FULL))
#define B1K (0)
#define B2K (1)
#define B4K (2)
#define B8K (3)
#define B32K (4)
#define SHARED_BUF_SIZES (B32K+1)
#define MAX_MALLOC_BUFS (2000)
#define MAX_CACHE_SIZE (KILOBYTES(32))
struct shared_bufs {
char buf_1K[NUMBER_1K_BUFS][1024];
char buf_2K[NUMBER_2K_BUFS][2048];
char buf_4K[NUMBER_4K_BUFS][4096];
char buf_8K[NUMBER_8K_BUFS][8192];
char buf_32K[NUMBER_32K_BUFS][32768];
long buf_1K_used;
long buf_2K_used;
long buf_4K_used;
long buf_8K_used;
long buf_32K_used;
long buf_1K_maxuse;
long buf_2K_maxuse;
long buf_4K_maxuse;
long buf_8K_maxuse;
long buf_32K_maxuse;
long buf_1K_ovf;
long buf_2K_ovf;
long buf_4K_ovf;
long buf_8K_ovf;
long buf_32K_ovf;
int buf_inuse[SHARED_BUF_SIZES];
char *malloc_bp[MAX_MALLOC_BUFS];
long smallest;
long largest;
long embedded;
long max_embedded;
long mallocs;
long frees;
double total;
ulong reqs;
} shared_bufs;
void
buf_init(void)
{
struct shared_bufs *bp;
bp = &shared_bufs;
BZERO(bp, sizeof(struct shared_bufs));
bp->smallest = 0x7fffffff;
bp->total = 0.0;
#ifdef VALGRIND
VALGRIND_MAKE_MEM_NOACCESS(&bp->buf_1K, sizeof(bp->buf_1K));
VALGRIND_MAKE_MEM_NOACCESS(&bp->buf_2K, sizeof(bp->buf_2K));
VALGRIND_MAKE_MEM_NOACCESS(&bp->buf_4K, sizeof(bp->buf_4K));
VALGRIND_MAKE_MEM_NOACCESS(&bp->buf_8K, sizeof(bp->buf_8K));
VALGRIND_MAKE_MEM_NOACCESS(&bp->buf_32K, sizeof(bp->buf_32K));
VALGRIND_CREATE_MEMPOOL(&bp->buf_1K, 0, 1);
VALGRIND_CREATE_MEMPOOL(&bp->buf_2K, 0, 1);
VALGRIND_CREATE_MEMPOOL(&bp->buf_4K, 0, 1);
VALGRIND_CREATE_MEMPOOL(&bp->buf_8K, 0, 1);
VALGRIND_CREATE_MEMPOOL(&bp->buf_32K, 0, 1);
#endif
}
/*
* Free up all buffers used by the last command.
*/
void free_all_bufs(void)
{
int i;
struct shared_bufs *bp;
bp = &shared_bufs;
bp->embedded = 0;
for (i = 0; i < SHARED_BUF_SIZES; i++)
bp->buf_inuse[i] = 0;
for (i = 0; i < MAX_MALLOC_BUFS; i++) {
if (bp->malloc_bp[i]) {
free(bp->malloc_bp[i]);
bp->malloc_bp[i] = NULL;
bp->frees++;
}
}
if (bp->mallocs != bp->frees)
error(WARNING, "malloc/free mismatch (%ld/%ld)\n",
bp->mallocs, bp->frees);
#ifdef VALGRIND
VALGRIND_DESTROY_MEMPOOL(&bp->buf_1K);
VALGRIND_DESTROY_MEMPOOL(&bp->buf_2K);
VALGRIND_DESTROY_MEMPOOL(&bp->buf_4K);
VALGRIND_DESTROY_MEMPOOL(&bp->buf_8K);
VALGRIND_DESTROY_MEMPOOL(&bp->buf_32K);
VALGRIND_MAKE_MEM_NOACCESS(&bp->buf_1K, sizeof(bp->buf_1K));
VALGRIND_MAKE_MEM_NOACCESS(&bp->buf_2K, sizeof(bp->buf_2K));
VALGRIND_MAKE_MEM_NOACCESS(&bp->buf_4K, sizeof(bp->buf_4K));
VALGRIND_MAKE_MEM_NOACCESS(&bp->buf_8K, sizeof(bp->buf_8K));
VALGRIND_MAKE_MEM_NOACCESS(&bp->buf_32K, sizeof(bp->buf_32K));
VALGRIND_CREATE_MEMPOOL(&bp->buf_1K, 0, 1);
VALGRIND_CREATE_MEMPOOL(&bp->buf_2K, 0, 1);
VALGRIND_CREATE_MEMPOOL(&bp->buf_4K, 0, 1);
VALGRIND_CREATE_MEMPOOL(&bp->buf_8K, 0, 1);
VALGRIND_CREATE_MEMPOOL(&bp->buf_32K, 0, 1);
#endif
}
/*
* Free a specific buffer that may have been returned by malloc().
* If the address is one of the static buffers, look for it and
* clear its inuse bit.
*/
void
freebuf(char *addr)
{
int i;
struct shared_bufs *bp;
bp = &shared_bufs;
bp->embedded--;
if (CRASHDEBUG(8)) {
INDENT(bp->embedded*2);
fprintf(fp, "FREEBUF(%ld)\n", bp->embedded);
}
for (i = 0; i < NUMBER_1K_BUFS; i++) {
if (addr == (char *)&bp->buf_1K[i]) {
bp->buf_inuse[B1K] &= ~(1 << i);
#ifdef VALGRIND
VALGRIND_MEMPOOL_FREE(&bp->buf_1K, addr);
#endif
return;
}
}
for (i = 0; i < NUMBER_2K_BUFS; i++) {
if (addr == (char *)&bp->buf_2K[i]) {
bp->buf_inuse[B2K] &= ~(1 << i);
#ifdef VALGRIND
VALGRIND_MEMPOOL_FREE(&bp->buf_2K, addr);
#endif
return;
}
}
for (i = 0; i < NUMBER_4K_BUFS; i++) {
if (addr == (char *)&bp->buf_4K[i]) {
bp->buf_inuse[B4K] &= ~(1 << i);
#ifdef VALGRIND
VALGRIND_MEMPOOL_FREE(&bp->buf_4K, addr);
#endif
return;
}
}
for (i = 0; i < NUMBER_8K_BUFS; i++) {
if (addr == (char *)&bp->buf_8K[i]) {
bp->buf_inuse[B8K] &= ~(1 << i);
#ifdef VALGRIND
VALGRIND_MEMPOOL_FREE(&bp->buf_8K, addr);
#endif
return;
}
}
for (i = 0; i < NUMBER_32K_BUFS; i++) {
if (addr == (char *)&bp->buf_32K[i]) {
bp->buf_inuse[B32K] &= ~(1 << i);
#ifdef VALGRIND
VALGRIND_MEMPOOL_FREE(&bp->buf_32K, addr);
#endif
return;
}
}
for (i = 0; i < MAX_MALLOC_BUFS; i++) {
if (bp->malloc_bp[i] == addr) {
free(bp->malloc_bp[i]);
bp->malloc_bp[i] = NULL;
bp->frees++;
return;
}
}
error(FATAL,
"freeing an unknown buffer -- shared buffer inconsistency!\n");
}
/* DEBUG */
void
dump_embedded(char *s)
{
struct shared_bufs *bp;
char *p1;
p1 = s ? s : "";
bp = &shared_bufs;
console("%s: embedded: %ld mallocs: %ld frees: %ld\n",
p1, bp->embedded, bp->mallocs, bp->frees);
}
/* DEBUG */
long
get_embedded(void)
{
struct shared_bufs *bp;
bp = &shared_bufs;
return(bp->embedded);
}
/*
* "help -b" output
*/
void
dump_shared_bufs(void)
{
int i;
struct shared_bufs *bp;
bp = &shared_bufs;
fprintf(fp, " buf_1K_used: %ld\n", bp->buf_1K_used);
fprintf(fp, " buf_2K_used: %ld\n", bp->buf_2K_used);
fprintf(fp, " buf_4K_used: %ld\n", bp->buf_4K_used);
fprintf(fp, " buf_8K_used: %ld\n", bp->buf_8K_used);
fprintf(fp, " buf_32K_used: %ld\n", bp->buf_32K_used);
fprintf(fp, " buf_1K_ovf: %ld\n", bp->buf_1K_ovf);
fprintf(fp, " buf_2K_ovf: %ld\n", bp->buf_2K_ovf);
fprintf(fp, " buf_4K_ovf: %ld\n", bp->buf_4K_ovf);
fprintf(fp, " buf_8K_ovf: %ld\n", bp->buf_8K_ovf);
fprintf(fp, " buf_32K_ovf: %ld\n", bp->buf_32K_ovf);
fprintf(fp, " buf_1K_maxuse: %2ld of %d\n", bp->buf_1K_maxuse,
NUMBER_1K_BUFS);
fprintf(fp, " buf_2K_maxuse: %2ld of %d\n", bp->buf_2K_maxuse,
NUMBER_2K_BUFS);
fprintf(fp, " buf_4K_maxuse: %2ld of %d\n", bp->buf_4K_maxuse,
NUMBER_4K_BUFS);
fprintf(fp, " buf_8K_maxuse: %2ld of %d\n", bp->buf_8K_maxuse,
NUMBER_8K_BUFS);
fprintf(fp, "buf_32K_maxuse: %2ld of %d\n", bp->buf_32K_maxuse,
NUMBER_32K_BUFS);
fprintf(fp, " buf_inuse[%d]: ", SHARED_BUF_SIZES);
for (i = 0; i < SHARED_BUF_SIZES; i++)
fprintf(fp, "[%lx]", (ulong)bp->buf_inuse[i]);
fprintf(fp, "\n");
for (i = 0; i < MAX_MALLOC_BUFS; i++)
if (bp->malloc_bp[i])
fprintf(fp, " malloc_bp[%d]: %lx\n",
i, (ulong)bp->malloc_bp[i]);
if (bp->smallest == 0x7fffffff)
fprintf(fp, " smallest: 0\n");
else
fprintf(fp, " smallest: %ld\n", bp->smallest);
fprintf(fp, " largest: %ld\n", bp->largest);
fprintf(fp, " embedded: %ld\n", bp->embedded);
fprintf(fp, " max_embedded: %ld\n", bp->max_embedded);
fprintf(fp, " mallocs: %ld\n", bp->mallocs);
fprintf(fp, " frees: %ld\n", bp->frees);
fprintf(fp, " reqs/total: %ld/%.0f\n", bp->reqs, bp->total);
fprintf(fp, " average size: %.0f\n", bp->total/bp->reqs);
}
/*
* Try to get one of the static buffers first. If not available, fall
* through and get it from malloc(), keeping trace of the returned address.
*/
#define SHARED_BUFSIZE(size) \
((size <= 1024) ? 1024 >> 7 : \
((size <= 2048) ? 2048 >> 7 : \
((size <= 4096) ? 4096 >> 7 : \
((size <= 8192) ? 8192 >> 7 : \
((size <= 32768) ? 32768 >> 7 : -1)))))
char *
getbuf(long reqsize)
{
int i;
int index;
int bdx;
int mask;
struct shared_bufs *bp;
char *bufp;
if (!reqsize) {
ulong retaddr = (ulong)__builtin_return_address(0);
error(FATAL, "zero-size memory allocation! (called from %lx)\n",
retaddr);
}
bp = &shared_bufs;
index = SHARED_BUFSIZE(reqsize);
if (CRASHDEBUG(7) && (reqsize > MAX_CACHE_SIZE))
error(NOTE, "GETBUF request > MAX_CACHE_SIZE: %ld\n",
reqsize);
if (CRASHDEBUG(8)) {
INDENT(bp->embedded*2);
fprintf(fp, "GETBUF(%ld -> %ld)\n", reqsize, bp->embedded);
}
bp->embedded++;
if (bp->embedded > bp->max_embedded)
bp->max_embedded = bp->embedded;
if (reqsize < bp->smallest)
bp->smallest = reqsize;
if (reqsize > bp->largest)
bp->largest = reqsize;
bp->total += reqsize;
bp->reqs++;
switch (index)
{
case -1:
break;
case 8:
if (SHARED_1K_BUF_AVAIL(bp->buf_inuse[B1K])) {
mask = ~(bp->buf_inuse[B1K]);
bdx = ffs(mask) - 1;
bufp = bp->buf_1K[bdx];
bp->buf_1K_used++;
bp->buf_inuse[B1K] |= (1 << bdx);
bp->buf_1K_maxuse = MAX(bp->buf_1K_maxuse,
count_bits_int(bp->buf_inuse[B1K]));
#ifdef VALGRIND
VALGRIND_MEMPOOL_ALLOC(&bp->buf_1K, bufp, 1024);
#endif
BZERO(bufp, 1024);
return(bufp);
}
bp->buf_1K_ovf++; /* FALLTHROUGH */
case 16:
if (SHARED_2K_BUF_AVAIL(bp->buf_inuse[B2K])) {
mask = ~(bp->buf_inuse[B2K]);
bdx = ffs(mask) - 1;
bufp = bp->buf_2K[bdx];
bp->buf_2K_used++;
bp->buf_inuse[B2K] |= (1 << bdx);
bp->buf_2K_maxuse = MAX(bp->buf_2K_maxuse,
count_bits_int(bp->buf_inuse[B2K]));
#ifdef VALGRIND
VALGRIND_MEMPOOL_ALLOC(&bp->buf_2K, bufp, 2048);
#endif
BZERO(bufp, 2048);
return(bufp);
}
bp->buf_2K_ovf++; /* FALLTHROUGH */
case 32:
if (SHARED_4K_BUF_AVAIL(bp->buf_inuse[B4K])) {
mask = ~(bp->buf_inuse[B4K]);
bdx = ffs(mask) - 1;
bufp = bp->buf_4K[bdx];
bp->buf_4K_used++;
bp->buf_inuse[B4K] |= (1 << bdx);
bp->buf_4K_maxuse = MAX(bp->buf_4K_maxuse,
count_bits_int(bp->buf_inuse[B4K]));
#ifdef VALGRIND
VALGRIND_MEMPOOL_ALLOC(&bp->buf_4K, bufp, 4096);
#endif
BZERO(bufp, 4096);
return(bufp);
}
bp->buf_4K_ovf++; /* FALLTHROUGH */
case 64:
if (SHARED_8K_BUF_AVAIL(bp->buf_inuse[B8K])) {
mask = ~(bp->buf_inuse[B8K]);
bdx = ffs(mask) - 1;
bufp = bp->buf_8K[bdx];
bp->buf_8K_used++;
bp->buf_inuse[B8K] |= (1 << bdx);
bp->buf_8K_maxuse = MAX(bp->buf_8K_maxuse,
count_bits_int(bp->buf_inuse[B8K]));
#ifdef VALGRIND
VALGRIND_MEMPOOL_ALLOC(&bp->buf_8K, bufp, 8192);
#endif
BZERO(bufp, 8192);
return(bufp);
}
bp->buf_8K_ovf++; /* FALLTHROUGH */
case 256:
if (SHARED_32K_BUF_AVAIL(bp->buf_inuse[B32K])) {
mask = ~(bp->buf_inuse[B32K]);
bdx = ffs(mask) - 1;
bufp = bp->buf_32K[bdx];
bp->buf_32K_used++;
bp->buf_inuse[B32K] |= (1 << bdx);
bp->buf_32K_maxuse = MAX(bp->buf_32K_maxuse,
count_bits_int(bp->buf_inuse[B32K]));
#ifdef VALGRIND
VALGRIND_MEMPOOL_ALLOC(&bp->buf_32K, bufp, 32768);
#endif
BZERO(bufp, 32768);
return(bufp);
}
bp->buf_32K_ovf++;
break;
}
for (i = 0; i < MAX_MALLOC_BUFS; i++) {
if (bp->malloc_bp[i])
continue;
if ((bp->malloc_bp[i] = (char *)calloc(reqsize, 1))) {
bp->mallocs++;
return(bp->malloc_bp[i]);
}
break;
}
dump_shared_bufs();
return ((char *)(long)
error(FATAL, "cannot allocate any more memory!\n"));
}
/*
* Change the size of the previously-allocated memory block
* pointed to by oldbuf to newsize bytes. Copy the minimum
* of oldsize and newsize bytes from the oldbuf to the newbuf,
* and return the address of the new buffer, which will have
* a different address than oldbuf.
*/
char *
resizebuf(char *oldbuf, long oldsize, long newsize)
{
char *newbuf;
newbuf = GETBUF(newsize);
BCOPY(oldbuf, newbuf, MIN(oldsize, newsize));
FREEBUF(oldbuf);
return newbuf;
}
/*
* Duplicate a string into a buffer allocated with GETBUF().
*/
char *
strdupbuf(char *oldstring)
{
char *newstring;
newstring = GETBUF(strlen(oldstring)+1);
strcpy(newstring, oldstring);
return newstring;
}
/*
* Return the number of bits set in an int or long.
*/
int
count_bits_int(int val)
{
int i, cnt;
int total;
cnt = sizeof(int) * 8;
for (i = total = 0; i < cnt; i++) {
if (val & 1)
total++;
val >>= 1;
}
return total;
}
int
count_bits_long(ulong val)
{
int i, cnt;
int total;
cnt = sizeof(long) * 8;
for (i = total = 0; i < cnt; i++) {
if (val & 1)
total++;
val >>= 1;
}
return total;
}
int
highest_bit_long(ulong val)
{
int i, cnt;
int total;
int highest;
highest = -1;
cnt = sizeof(long) * 8;
for (i = total = 0; i < cnt; i++) {
if (val & 1)
highest = i;
val >>= 1;
}
return highest;
}
int
lowest_bit_long(ulong val)
{
int i, cnt;
int lowest;
lowest = -1;
cnt = sizeof(long) * 8;
for (i = 0; i < cnt; i++) {
if (val & 1) {
lowest = i;
break;
}
val >>= 1;
}
return lowest;
}
/*
* Debug routine to stop whatever's going on in its tracks.
*/
void
drop_core(char *s)
{
volatile int *nullptr;
int i ATTRIBUTE_UNUSED;
if (s && ascii_string(s))
fprintf(stderr, "%s", s);
kill((pid_t)pc->program_pid, 3);
nullptr = NULL;
while (TRUE)
i = *nullptr;
}
/*
* For debug output to a device other than the current terminal.
* pc->console must have been preset by:
*
* 1. by an .rc file setting: "set console /dev/whatever"
* 2. by a runtime command: "set console /dev/whatever"
* 3. during program invocation: "-c /dev/whatever"
*
* The first time it's called, the device will be opened.
*/
int
console(const char *fmt, ...)
{
char output[BUFSIZE*2];
va_list ap;
if (!pc->console || !strlen(pc->console) ||
(pc->flags & NO_CONSOLE) || (pc->confd == -1))
return 0;
if (!fmt || !strlen(fmt))
return 0;
va_start(ap, fmt);
(void)vsnprintf(output, BUFSIZE*2, fmt, ap);
va_end(ap);
if (pc->confd == -2) {
if ((pc->confd = open(pc->console, O_WRONLY|O_NDELAY)) < 0) {
error(INFO, "console device %s: %s\n",
pc->console, strerror(errno), 0, 0);
return 0;
}
}
return(write(pc->confd, output, strlen(output)));
}
/*
* Allocate space to store the designated console device name.
* If a console device pre-exists, free its name space and close the device.
*/
void
create_console_device(char *dev)
{
if (pc->console) {
if (pc->confd != -1)
close(pc->confd);
free(pc->console);
}
pc->confd = -2;
if ((pc->console = (char *)malloc(strlen(dev)+1)) == NULL)
fprintf(stderr, "console name malloc: %s\n", strerror(errno));
else {
strcpy(pc->console, dev);
if (console("debug console [%ld]: %s\n",
pc->program_pid, (ulong)pc->console) < 0) {
close(pc->confd);
free(pc->console);
pc->console = NULL;
pc->confd = -1;
if (!(pc->flags & RUNTIME))
error(INFO, "cannot set console to %s\n", dev);
}
}
}
/*
* Disable console output without closing the device.
* Typically used with CONSOLE_OFF() macro.
*/
int
console_off(void)
{
int orig_no_console;
orig_no_console = pc->flags & NO_CONSOLE;
pc->flags |= NO_CONSOLE;
return orig_no_console;
}
/*
* Re-enable console output. Typically used with CONSOLE_ON() macro.
*/
int
console_on(int orig_no_console)
{
if (!orig_no_console)
pc->flags &= ~NO_CONSOLE;
return(pc->flags & NO_CONSOLE);
}
/*
* Print a string to the console device with no formatting, useful for
* sending strings containing % signs.
*/
int
console_verbatim(char *s)
{
char *p;
int cnt;
if (!pc->console || !strlen(pc->console) ||
(pc->flags & NO_CONSOLE) || (pc->confd == -1))
return 0;
if (!s || !strlen(s))
return 0;
if (pc->confd == -2) {
if ((pc->confd = open(pc->console, O_WRONLY|O_NDELAY)) < 0) {
fprintf(stderr, "%s: %s\n",
pc->console, strerror(errno));
return 0;
}
}
for (cnt = 0, p = s; *p; p++) {
if (write(pc->confd, p, 1) != 1)
break;
cnt++;
}
return cnt;
}
/*
* Set up a signal handler.
*/
void
sigsetup(int sig, void *handler, struct sigaction *act,struct sigaction *oldact)
{
BZERO(act, sizeof(struct sigaction));
act->sa_handler = handler;
act->sa_flags = SA_NOMASK;
sigaction(sig, act, oldact);
}
/*
* Convert a jiffies-based time value into a string showing the
* the number of days, hours:minutes:seconds.
*/
#define SEC_MINUTES (60)
#define SEC_HOURS (60 * SEC_MINUTES)
#define SEC_DAYS (24 * SEC_HOURS)
char *
convert_time(ulonglong count, char *buf)
{
ulonglong total, days, hours, minutes, seconds;
if (CRASHDEBUG(2))
error(INFO, "convert_time: %lld (%llx)\n", count, count);
if (!machdep->hz) {
sprintf(buf, "(cannot calculate: unknown HZ value)");
return buf;
}
total = (count)/(ulonglong)machdep->hz;
days = total / SEC_DAYS;
total %= SEC_DAYS;
hours = total / SEC_HOURS;
total %= SEC_HOURS;
minutes = total / SEC_MINUTES;
seconds = total % SEC_MINUTES;
buf[0] = NULLCHAR;
if (days)
sprintf(buf, "%llu days, ", days);
sprintf(&buf[strlen(buf)], "%02llu:%02llu:%02llu",
hours, minutes, seconds);
return buf;
}
/*
* Convert a calendar time into a null-terminated string like ctime(), but
* the result string contains the time zone string and does not ends with a
* linefeed ('\n'). If localtime() or strftime() fails, fails back to return
* POSIX time (seconds since the Epoch) or ctime() string respectively.
*
* NOTE: The return value points to a statically allocated string which is
* overwritten by subsequent calls.
*/
char *
ctime_tz(time_t *timep)
{
static char buf[64];
struct tm *tm;
size_t size;
if (!timep)
return NULL;
tm = localtime(timep);
if (!tm) {
snprintf(buf, sizeof(buf), "%ld", *timep);
return buf;
}
size = strftime(buf, sizeof(buf), "%a %b %e %T %Z %Y", tm);
if (!size)
return strip_linefeeds(ctime(timep));
return buf;
}
/*
* Stall for a number of microseconds.
*/
void
stall(ulong microseconds)
{
struct timeval delay;
delay.tv_sec = 0;
delay.tv_usec = (__time_t)microseconds;
(void) select(0, (fd_set *) 0, (fd_set *) 0, (fd_set *) 0, &delay);
}
/*
* Fill a buffer with a page count translated to a GB/MB/KB value.
*/
char *
pages_to_size(ulong pages, char *buf)
{
double total;
char *p;
if (pages == 0) {
sprintf(buf, "0");
return buf;
}
total = (double)pages * (double)PAGESIZE();
if (total >= GIGABYTES(1))
sprintf(buf, "%.1f GB", total/(double)GIGABYTES(1));
else if (total >= MEGABYTES(1))
sprintf(buf, "%.1f MB", total/(double)MEGABYTES(1));
else
sprintf(buf, "%ld KB", (ulong)(total/(double)KILOBYTES(1)));
if ((p = strstr(buf, ".0 ")))
memmove(p, p + 2, sizeof(" GB"));
return buf;
}
/*
* If the list_head.next value points to itself, it's an emtpy list.
*/
int
empty_list(ulong list_head_addr)
{
ulong next;
if (!readmem(list_head_addr, KVADDR, &next, sizeof(void *),
"list_head next contents", RETURN_ON_ERROR))
return TRUE;
return (next == list_head_addr);
}
int
machine_type(char *type)
{
return STREQ(MACHINE_TYPE, type);
}
int
machine_type_mismatch(char *file, char *e_machine, char *alt, ulong query)
{
if (machine_type(e_machine) || machine_type(alt))
return FALSE;
if (query == KDUMP_LOCAL) /* already printed by NETDUMP_LOCAL */
return TRUE;
error(WARNING, "machine type mismatch:\n");
fprintf(fp, " crash utility: %s\n", MACHINE_TYPE);
fprintf(fp, " %s: %s%s%s\n\n", file, e_machine,
alt ? " or " : "", alt ? alt : "");
return TRUE;
}
void
command_not_supported()
{
error(FATAL,
"command not supported or applicable on this architecture or kernel\n");
}
void
option_not_supported(int c)
{
error(FATAL,
"-%c option not supported or applicable on this architecture or kernel\n",
(char)c);
}
static int please_wait_len = 0;
void
please_wait(char *s)
{
int fd;
char buf[BUFSIZE];
if ((pc->flags & SILENT) || !DUMPFILE() || (pc->flags & RUNTIME))
return;
if (!(pc->flags & TTY) && KVMDUMP_DUMPFILE()) {
if (!isatty(fileno(stdin)) ||
((fd = open("/dev/tty", O_RDONLY)) < 0))
return;
close(fd);
}
pc->flags |= PLEASE_WAIT;
please_wait_len = sprintf(buf, "\rplease wait... (%s)", s);
fprintf(fp, "%s", buf);
fflush(fp);
}
void
please_wait_done(void)
{
if (!(pc->flags & PLEASE_WAIT))
return;
pc->flags &= ~PLEASE_WAIT;
fprintf(fp, "\r");
pad_line(fp, please_wait_len, ' ');
fprintf(fp, "\r");
fflush(fp);
}
/*
* Compare two pathnames.
*/
int
pathcmp(char *p1, char *p2)
{
char c1, c2;
do {
if ((c1 = *p1++) == '/')
while (*p1 == '/') { p1++; }
if ((c2 = *p2++) == '/')
while (*p2 == '/') { p2++; }
if (c1 == '\0')
return ((c2 == '/') && (*p2 == '\0')) ? 0 : c1 - c2;
} while (c1 == c2);
return ((c2 == '\0') && (c1 == '/') && (*p1 == '\0')) ? 0 : c1 - c2;
}
#include <elf.h>
/*
* Check the byte-order of an ELF file vs. the host byte order.
*/
int
endian_mismatch(char *file, char dumpfile_endian, ulong query)
{
char *endian;
switch (dumpfile_endian)
{
case ELFDATA2LSB:
if (__BYTE_ORDER == __LITTLE_ENDIAN)
return FALSE;
endian = "little-endian";
break;
case ELFDATA2MSB:
if (__BYTE_ORDER == __BIG_ENDIAN)
return FALSE;
endian = "big-endian";
break;
default:
endian = "unknown";
break;
}
if (query == KDUMP_LOCAL) /* already printed by NETDUMP_LOCAL */
return TRUE;
error(WARNING, "endian mismatch:\n");
fprintf(fp, " crash utility: %s\n",
(__BYTE_ORDER == __LITTLE_ENDIAN) ?
"little-endian" : "big-endian");
fprintf(fp, " %s: %s\n\n", file, endian);
return TRUE;
}
uint16_t
swap16(uint16_t val, int swap)
{
if (swap)
return (((val & 0x00ff) << 8) |
((val & 0xff00) >> 8));
else
return val;
}
uint32_t
swap32(uint32_t val, int swap)
{
if (swap)
return (((val & 0x000000ffU) << 24) |
((val & 0x0000ff00U) << 8) |
((val & 0x00ff0000U) >> 8) |
((val & 0xff000000U) >> 24));
else
return val;
}
uint64_t
swap64(uint64_t val, int swap)
{
if (swap)
return (((val & 0x00000000000000ffULL) << 56) |
((val & 0x000000000000ff00ULL) << 40) |
((val & 0x0000000000ff0000ULL) << 24) |
((val & 0x00000000ff000000ULL) << 8) |
((val & 0x000000ff00000000ULL) >> 8) |
((val & 0x0000ff0000000000ULL) >> 24) |
((val & 0x00ff000000000000ULL) >> 40) |
((val & 0xff00000000000000ULL) >> 56));
else
return val;
}
/*
* Get a sufficiently large buffer for cpumask.
* You should call FREEBUF() on the result when you no longer need it.
*/
ulong *
get_cpumask_buf(void)
{
int cpulen, len_cpumask;
cpulen = DIV_ROUND_UP(kt->cpus, BITS_PER_LONG) * sizeof(ulong);
len_cpumask = VALID_SIZE(cpumask_t) ? SIZE(cpumask_t) : 0;
if (len_cpumask > 0)
cpulen = len_cpumask > cpulen ? cpulen : len_cpumask;
return (ulong *)GETBUF(cpulen);
}
int
make_cpumask(char *s, ulong *mask, int flags, int *errptr)
{
char *p, *q, *orig;
int start, end;
int i;
if (s == NULL) {
if (!(flags & QUIET))
error(INFO, "make_cpumask: received NULL string\n");
orig = NULL;
goto make_cpumask_error;
}
orig = strdup(s);
p = strtok(s, ",");
while (p) {
s = strtok(NULL, "");
if (STREQ(p, "a") || STREQ(p, "all")) {
start = 0;
end = kt->cpus - 1;
} else {
start = end = -1;
q = strtok(p, "-");
start = dtoi(q, flags, errptr);
if ((q = strtok(NULL, "-")))
end = dtoi(q, flags, errptr);
if (end == -1)
end = start;
}
if ((start < 0) || (start >= kt->cpus) ||
(end < 0) || (end >= kt->cpus)) {
error(INFO, "invalid cpu specification: %s\n", orig);
goto make_cpumask_error;
}
for (i = start; i <= end; i++)
SET_BIT(mask, i);
p = strtok(s, ",");
}
free(orig);
return TRUE;
make_cpumask_error:
free(orig);
switch (flags & (FAULT_ON_ERROR|RETURN_ON_ERROR))
{
case FAULT_ON_ERROR:
RESTART();
case RETURN_ON_ERROR:
if (errptr)
*errptr = TRUE;
break;
}
return UNUSED;
}
/*
* Copy a string into a sized buffer. If necessary, truncate
* the resultant string in the sized buffer so that it will
* always be NULL-terminated.
*/
size_t
strlcpy(char *dest, const char *src, size_t size)
{
size_t ret = strlen(src);
if (size) {
size_t len = (ret >= size) ? size - 1 : ret;
memcpy(dest, src, len);
dest[len] = '\0';
}
return ret;
}
struct rb_node *
rb_first(struct rb_root *root)
{
struct rb_root rloc;
struct rb_node *n;
struct rb_node nloc;
readmem((ulong)root, KVADDR, &rloc, sizeof(struct rb_root),
"rb_root", FAULT_ON_ERROR);
n = rloc.rb_node;
if (!n)
return NULL;
while (rb_left(n, &nloc))
n = nloc.rb_left;
return n;
}
struct rb_node *
rb_parent(struct rb_node *node, struct rb_node *nloc)
{
readmem((ulong)node, KVADDR, nloc, sizeof(struct rb_node),
"rb_node", FAULT_ON_ERROR);
return (struct rb_node *)(nloc->rb_parent_color & ~3);
}
struct rb_node *
rb_right(struct rb_node *node, struct rb_node *nloc)
{
readmem((ulong)node, KVADDR, nloc, sizeof(struct rb_node),
"rb_node", FAULT_ON_ERROR);
return nloc->rb_right;
}
struct rb_node *
rb_left(struct rb_node *node, struct rb_node *nloc)
{
readmem((ulong)node, KVADDR, nloc, sizeof(struct rb_node),
"rb_node", FAULT_ON_ERROR);
return nloc->rb_left;
}
struct rb_node *
rb_next(struct rb_node *node)
{
struct rb_node nloc;
struct rb_node *parent;
/* node is destroyed */
if (!accessible((ulong)node))
return NULL;
parent = rb_parent(node, &nloc);
if (parent == node)
return NULL;
if (nloc.rb_right) {
/* rb_right is destroyed */
if (!accessible((ulong)nloc.rb_right))
return NULL;
node = nloc.rb_right;
while (rb_left(node, &nloc)) {
/* rb_left is destroyed */
if (!accessible((ulong)nloc.rb_left))
return NULL;
node = nloc.rb_left;
}
return node;
}
while ((parent = rb_parent(node, &nloc))) {
/* parent is destroyed */
if (!accessible((ulong)parent))
return NULL;
if (node != rb_right(parent, &nloc))
break;
node = parent;
}
return parent;
}
struct rb_node *
rb_last(struct rb_root *root)
{
struct rb_node *node;
struct rb_node nloc;
/* meet destroyed data */
if (!accessible((ulong)(root + OFFSET(rb_root_rb_node))))
return NULL;
readmem((ulong)(root + OFFSET(rb_root_rb_node)), KVADDR, &node,
sizeof(node), "rb_root node", FAULT_ON_ERROR);
while (1) {
if (!node)
break;
/* meet destroyed data */
if (!accessible((ulong)node))
return NULL;
readmem((ulong)node, KVADDR, &nloc, sizeof(struct rb_node),
"rb_node last", FAULT_ON_ERROR);
/* meet the last one */
if (!nloc.rb_right)
break;
/* meet destroyed data */
if (!!accessible((ulong)nloc.rb_right))
break;
node = nloc.rb_right;
}
return node;
}
long
percpu_counter_sum_positive(ulong fbc)
{
int i, count;
ulong addr;
long ret;
if (INVALID_MEMBER(percpu_counter_count))
return 0;
readmem(fbc + OFFSET(percpu_counter_count), KVADDR, &ret,
sizeof(long long), "percpu_counter.count", FAULT_ON_ERROR);
if (INVALID_MEMBER(percpu_counter_counters)) /* !CONFIG_SMP */
return (ret < 0) ? 0 : ret;
readmem(fbc + OFFSET(percpu_counter_counters), KVADDR, &addr,
sizeof(void *), "percpu_counter.counters", FAULT_ON_ERROR);
for (i = 0; i < kt->cpus; i++) {
readmem(addr + kt->__per_cpu_offset[i], KVADDR, &count,
sizeof(int), "percpu_counter.counters count", FAULT_ON_ERROR);
ret += count;
}
return (ret < 0) ? 0 : ret;
}
ulong
get_subsys_private(char *kset_name, char *target_name)
{
ulong kset_addr, kset_list, name_addr, private = 0;
struct list_data list_data, *ld;
char buf[32];
int i, cnt;
if (!symbol_exists(kset_name))
return 0;
ld = &list_data;
BZERO(ld, sizeof(struct list_data));
get_symbol_data(kset_name, sizeof(ulong), &kset_addr);
readmem(kset_addr + OFFSET(kset_list), KVADDR, &kset_list,
sizeof(ulong), "kset.list", FAULT_ON_ERROR);
ld->flags |= LIST_ALLOCATE;
ld->start = kset_list;
ld->end = kset_addr + OFFSET(kset_list);
ld->list_head_offset = OFFSET(kobject_entry);
cnt = do_list(ld);
for (i = 0; i < cnt; i++) {
readmem(ld->list_ptr[i] + OFFSET(kobject_name), KVADDR, &name_addr,
sizeof(ulong), "kobject.name", FAULT_ON_ERROR);
read_string(name_addr, buf, sizeof(buf)-1);
if (CRASHDEBUG(1))
fprintf(fp, "kobject: %lx name: %s\n", ld->list_ptr[i], buf);
if (STREQ(buf, target_name)) {
/* entry is subsys_private.subsys.kobj. See bus_to_subsys(). */
private = ld->list_ptr[i] - OFFSET(kset_kobj)
- OFFSET(subsys_private_subsys);
break;
}
}
FREEBUF(ld->list_ptr);
return private;
}