mirror of
https://github.com/crash-utility/crash
synced 2025-02-24 01:16:49 +00:00
by the "kmem" command when SLAB_RED_ZONE has been enabled in Linux 4.6 and later kernels. By default, CONFIG_SLUB object addresses that are displayed by the "kmem" command will point to the SLAB_RED_ZONE padding inserted at the beginning of the object. As an alternative, a new "redzone" environment variable has been addedd that can be toggled on or off. If "set redzone off" is entered, the object addresses will point to the address that gets returned to the allocator. (hirofumi@mail.parknet.co.jp, anderson@redhat.com)
6175 lines
151 KiB
C
6175 lines
151 KiB
C
/* tools.c - core analysis suite
|
|
*
|
|
* Copyright (C) 1999, 2000, 2001, 2002 Mission Critical Linux, Inc.
|
|
* Copyright (C) 2002-2017 David Anderson
|
|
* Copyright (C) 2002-2017 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>
|
|
|
|
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 *);
|
|
static 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);
|
|
static struct req_entry *fill_member_offsets(char *);
|
|
static void dump_struct_members_fast(struct req_entry *, int, ulong);
|
|
|
|
/*
|
|
* 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 (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(stdout, "\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) {
|
|
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(stdout, "%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(stdout);
|
|
}
|
|
|
|
if ((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 = expression = FALSE;
|
|
|
|
/*
|
|
* 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++;
|
|
}
|
|
|
|
if (!string && str[i] == '(') {
|
|
expression = TRUE;
|
|
}
|
|
|
|
|
|
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] = ' ';
|
|
}
|
|
if (expression) {
|
|
expression = FALSE;
|
|
while (str[i] != ')' && str[i] != NULLCHAR)
|
|
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(char *s, char *buf)
|
|
{
|
|
char *p1, *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));
|
|
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|LONG_HEX|INT_HEX|INT_DEC|LONGLONG_HEX|ZERO_FILL))
|
|
{
|
|
case LONG_DEC:
|
|
sprintf(s, "%lu", (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);
|
|
show_context(CURRENT_CONTEXT());
|
|
return;
|
|
}
|
|
|
|
if (!tt->panic_task) {
|
|
error(INFO, "no panic task found!\n");
|
|
return;
|
|
}
|
|
set_context(tt->panic_task, NO_PID);
|
|
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 (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))
|
|
show_context(CURRENT_CONTEXT());
|
|
break;
|
|
|
|
case STR_TASK:
|
|
task = value;
|
|
pid = NO_PID;
|
|
if (set_context(task, pid))
|
|
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");
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
* 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;
|
|
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, "Hhrs:S:e:o:xdl:")) != EOF) {
|
|
switch(c)
|
|
{
|
|
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_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_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
|
|
ld->start += ld->list_head_offset;
|
|
}
|
|
|
|
ld->flags &= ~(LIST_OFFSET_ENTERED|LIST_START_ENTERED);
|
|
ld->flags |= VERBOSE;
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
static 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 (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;
|
|
}
|
|
|
|
/*
|
|
* 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)
|
|
|
|
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;
|
|
|
|
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:pN")) != EOF) {
|
|
switch (c)
|
|
{
|
|
case 't':
|
|
if (type_flag & (RADIXTREE_REQUEST|RBTREE_REQUEST)) {
|
|
error(INFO, "multiple tree types may not be entered\n");
|
|
cmd_usage(pc->curcmd, SYNOPSIS);
|
|
}
|
|
|
|
if (STRNEQ(optarg, "ra"))
|
|
type_flag = RADIXTREE_REQUEST;
|
|
else if (STRNEQ(optarg, "rb"))
|
|
type_flag = RBTREE_REQUEST;
|
|
else {
|
|
error(INFO, "invalid tree type: %s\n", optarg);
|
|
cmd_usage(pc->curcmd, SYNOPSIS);
|
|
}
|
|
|
|
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;
|
|
default:
|
|
argerrs++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (argerrs)
|
|
cmd_usage(pc->curcmd, SYNOPSIS);
|
|
|
|
if ((type_flag & RADIXTREE_REQUEST) && (td->flags & TREE_NODE_OFFSET_ENTERED))
|
|
error(FATAL, "-o option is not applicable to radix trees\n");
|
|
|
|
if ((td->flags & TREE_ROOT_OFFSET_ENTERED) &&
|
|
(td->flags & TREE_NODE_POINTER))
|
|
error(INFO, "-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++ ? "|" : "");
|
|
fprintf(fp, ")\n");
|
|
fprintf(fp, " type: %s\n",
|
|
type_flag & RADIXTREE_REQUEST ? "radix" : "red-black");
|
|
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
|
|
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;
|
|
|
|
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 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]);
|
|
}
|
|
|
|
if (hq_enter(slot))
|
|
td->count++;
|
|
else
|
|
error(FATAL,
|
|
"\nduplicate tree entry: radix_tree_node: %lx slots[%d]: %lx\n",
|
|
node, index, slot);
|
|
|
|
if (td->flags & VERBOSE)
|
|
fprintf(fp, "%lx\n", slot);
|
|
|
|
if (td->flags & TREE_POSITION_DISPLAY) {
|
|
fprintf(fp, " position: %s/%ld\n",
|
|
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;
|
|
}
|
|
|
|
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, left_p, right_p;
|
|
char left_pos[BUFSIZE], right_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);
|
|
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
readmem(node_p+OFFSET(rb_node_rb_left), KVADDR, &left_p,
|
|
sizeof(void *), "rb_node rb_left", FAULT_ON_ERROR);
|
|
readmem(node_p+OFFSET(rb_node_rb_right), KVADDR, &right_p,
|
|
sizeof(void *), "rb_node rb_right", FAULT_ON_ERROR);
|
|
|
|
sprintf(left_pos, "%s/l", pos);
|
|
sprintf(right_pos, "%s/r", pos);
|
|
|
|
rbtree_iteration(left_p, td, left_pos);
|
|
rbtree_iteration(right_p, td, right_pos);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
/*
|
|
* 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);
|
|
}
|
|
|
|
/*
|
|
* 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);
|
|
return;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < NUMBER_2K_BUFS; i++) {
|
|
if (addr == (char *)&bp->buf_2K[i]) {
|
|
bp->buf_inuse[B2K] &= ~(1 << i);
|
|
return;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < NUMBER_4K_BUFS; i++) {
|
|
if (addr == (char *)&bp->buf_4K[i]) {
|
|
bp->buf_inuse[B4K] &= ~(1 << i);
|
|
return;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < NUMBER_8K_BUFS; i++) {
|
|
if (addr == (char *)&bp->buf_8K[i]) {
|
|
bp->buf_inuse[B8K] &= ~(1 << i);
|
|
return;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < NUMBER_32K_BUFS; i++) {
|
|
if (addr == (char *)&bp->buf_32K[i]) {
|
|
bp->buf_inuse[B32K] &= ~(1 << i);
|
|
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]));
|
|
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]));
|
|
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]));
|
|
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]));
|
|
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]));
|
|
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(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;
|
|
}
|
|
|
|
/*
|
|
* 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 *p1, *p2;
|
|
|
|
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 ((p1 = strstr(buf, ".0 "))) {
|
|
p2 = p1 + 3;
|
|
*p1++ = ' ';
|
|
strcpy(p1, p2);
|
|
}
|
|
|
|
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;
|
|
if ((cpulen = STRUCT_SIZE("cpumask_t")) < 0)
|
|
cpulen = DIV_ROUND_UP(kt->cpus, BITS_PER_LONG) * sizeof(ulong);
|
|
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, 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;
|
|
}
|