psyclpc/mudlib/uni-crasher/crasher.c

832 lines
20 KiB
C
Raw Blame History

/*
crasher: Standardversion (Menaures 07.12.01)
ANLEITUNG:
1. Bei dem Define DEBUGGER den eigenen Real-Namen angeben
2. Define VERBOSE auskommentieren, falls nicht bereits so.
VERBOSE kann genutzt werden, wenn gesicherte Dateien einen
Crash ausloesen und die genaue Zeile gesucht wird.
(VERBOSE gibt vor jedem einzelnen Aufruf momentane
Datei und Zeile per debug_message aus).
3. Wenn bereits aufgenommene Dateien existieren, das EFuns und
Values-Array NICHT modifizieren, bzw. nicht die Positionen
der einzelnen Elemente aendern - sonst werden die aufgezeichneten
Dateien unbrauchbar. Ebenso koennen nach solchen Modifikationen
aufgenommene Dateien nicht mehr von alten Versionen des Crashers
abgespielt werden.
Wer andere Values / Closures haben moechte, modifiziere die
entsprechenden Arrays, speichere den Crasher unter einem anderen
Filenamen und mache einen eindeutigen Eintrag im Header...
record:
record(file) speichert Ausfuehrungsablaeufe in Dateien namens
file_xxxx, wobei xxxx eine Durchlaufnummer ist. Das Objekt
braucht fuer diese Dateien logischerweise Schreibrecht.
Die aufgenommenen Dateien werden nach ihrer Erstellung automatisch
ausgefuehrt. Fuehrt dies zu einem Crash, kann man das ganze einfach
nochmals abspielen.
replay:
replay(file) spielt aufgenommene Dateien ab, fuehrt sie also aus.
Dies ist dazu da, um Crashes, die dieses Objekt erzeugt, zu
reproduzieren. Statt file wird file_xxxx eingelesen, wobei xxxx
eine Durchlaufnummer ist.
execute_file:
Mit execute_file(file) kann man eine Datei gezielt ausfuehren.
Dazu gibt man den echten Dateinamen der Datei an, inklusive
Durchlaufnummer.
debug_query:
Mit debug_query(file, line) kann man abfragen, was in der Datei
file in Zeile line ausgefuehrt werden wuerde - also welche
Closure mit welchen Argumenten.
Hierzu muss der eigene Name als DEBUGGER definiert sein.
debug_exec:
Mit debug_exec(file, line) wird die angegebene Zeile in der
Datei ausgefuehrt. Dadurch kann gezielt festgestellt werden,
ob der einzelne Aufruf den Crash ausgeloest hat.
Crash-Grund finden:
Bringt der Crasher den Driver zum Absturz, zunaechst den Driver
neu starten und mit execute_file das zuletzt aufgenommene File
abspielen.
Bringt das den Driver zum Crash, den gleichen Vorgang mit
aktiviertem VERBOSE wiederholen, um die Zeilennummer herauszufinden.
Hat man erst einmal die Zeilennummer, laesst sich per debug_query
herausfinden, was ausgefuehrt werden, und mit debug_exec pruefen,
ob es wirklich der Ausloeser war.
Kann der Crash auf diese Weise nicht reproduziert werden, kann man
einmal versuchen, alle aufgenommenen Dateien mit replay abzuspielen;
fuehrt das zu keinem Crash, wurde der Crash nicht durch einen
bestimmten Aufruf (oder eine bestimmte Folge selbiger) ausgeloest,
sondern hat einen anderen Grund, der genauer untersucht werden muss.
*/
#include <math.h>
#define DEBUGGER "menaures"
#include <debug.h>
// #define VERBOSE // Aktivieren, wenn man die crashausloesende Zeile sucht
#define MAX_ARGS 5
#define DELAY 4
#define ITERATIONS 1000
mixed execute;
mixed efuns;
mixed values;
mixed names;
void record(string file);
void reset_all()
{
catch(
execute = 0,
( efuns =
({
#'Name,
#'[,
#'abs,
#'abs_path,
#'acos,
#'add_action,
#'add_dot_to_msg,
// #'add_verb,
// #'add_xverb,
#'all_environment,
#'all_inventory,
#'allocate,
// #'allocate_mapping,
#'and_bits,
#'apply,
#'arr_delete,
#'asin,
// #'assoc,
#'atan,
#'atan2,
#'attach_erq_demon,
#'auto_owner_search,
#'binary_message,
#'bind_lambda,
// #'break_point,
#'call_other,
#'call_out,
#'call_out_info,
#'call_resolved,
#'caller_stack,
#'caller_stack_depth,
#'capitalize,
#'cat,
#'catch,
#'ceil,
#'center,
#'clear_bit,
#'clock,
#'clone_object,
#'clonep,
// #'clones,
#'closurep,
#'command,
#'command_stack,
#'command_stack_depth,
#'cond_present,
#'convert_message,
#'convert_umlaute,
#'copies,
#'copy,
#'copy_bits,
#'copy_file,
// #'copy_mapping,
#'cos,
#'count_bits,
#'crypt,
#'ctime,
// #'db_affected_rows,
// #'db_close,
// #'db_coldefs,
// #'db_connect,
// #'db_conv_string,
// #'db_error,
// #'db_exec,
// #'db_fetch,
// #'db_handles,
// #'db_insert_id,
#'debug_info,
#'debug_message,
#'deep_copy,
#'deep_inventory,
#'deep_present,
#'dein,
#'deinem,
#'deinen,
#'deines,
#'dem,
#'den,
#'der,
#'des,
#'destruct,
#'diesem,
#'diesen,
#'dieser,
#'dieses,
#'disable_commands,
#'domain2map,
#'ed,
// #'efun308,
#'ein,
#'einem,
#'einen,
#'eines,
#'enable_commands,
#'environment,
#'er,
#'exec,
#'execute_command,
#'exp,
#'expand_define,
#'explode,
#'export_uid,
#'extern_call,
// #'extract,
// #'file_name,
#'file_path,
#'file_size,
#'file_time,
#'filter,
// #'filter_array,
#'filter_indices,
// #'filter_mapping,
#'filter_objects,
#'find_call_out,
#'find_living,
#'find_object,
#'find_player,
#'first_inventory,
#'floatp,
#'floor,
#'format_seconds,
#'format_vseconds,
#'funcall,
#'function_exists,
#'functionlist,
#'garbage_collection,
#'get_align_string,
#'get_dir,
#'get_error_file,
#'get_eval_cost,
#'get_extra_wizinfo,
#'get_genitiv,
#'get_type_info,
#'get_unique_string,
#'geteuid,
#'getuid,
#'gmtime,
#'heart_beat_info,
#'ihm,
#'ihn,
#'ihr,
#'ihrem,
#'ihren,
#'ihres,
#'implode,
#'inherit_list,
#'input_to,
// #'insert_alist,
#'interactive,
// #'intersect_alist,
#'intp,
#'invert_bits,
#'ist,
#'lambda,
#'last_bit,
#'last_instructions,
#'left,
#'limited,
#'liste,
#'living,
#'load_name,
#'load_object,
#'localtime,
#'log,
#'log_file,
#'lower_case,
#'m_allocate,
#'m_contains,
#'m_delete,
#'m_indices,
#'m_reallocate,
// #'m_sizeof,
#'m_values,
#'make_shared_string,
#'map,
#'map2domain,
// #'map_array,
#'map_indices,
// #'map_mapping,
#'map_object,
#'map_objects,
// #'mapping_contains,
#'mappingp,
#'max,
#'md5,
#'member,
// #'member_array,
#'min,
#'mixed2str,
#'mixed_to_closure,
#'mkdir,
#'mkmapping,
#'move_object,
#'negate,
#'next_bit,
#'next_inventory,
#'not_alone,
#'notify_fail,
#'object_info,
#'object_name,
#'object_time,
#'objectp,
#'or_bits,
// #'order_alist,
#'parse_com,
#'parse_com_error,
#'parse_com_error_string,
#'player_exists,
#'player_present,
#'playerp,
#'plural,
#'pointerp,
#'pol2xy,
#'pow,
#'present,
#'present_clone,
#'previous_object,
#'printf,
#'process_string,
#'program_name,
#'program_time,
#'query_actions,
// #'query_akkusativ,
#'query_command,
// #'query_dativ,
#'query_deklin,
#'query_deklin_adjektiv,
#'query_deklin_ein_adjektiv,
#'query_deklin_name,
// #'query_dekliniert,
#'query_editing,
// #'query_genitiv,
#'query_idle,
// #'query_imp_port,
#'query_input_pending,
#'query_ip_name,
#'query_ip_number,
#'query_limits,
#'query_living_name,
#'query_livings,
#'query_load_average,
#'query_mud_port,
#'query_notify_fail,
#'query_once_interactive,
#'query_pronom,
#'query_real_player_level,
#'query_shadowing,
#'query_snoop,
#'query_udp_port,
#'query_up_time,
#'query_verb,
#'quote,
#'raise_error,
#'random,
#'read_bytes,
#'read_file,
#'real_time_diff,
#'referencep,
#'regexp,
#'regexplode,
#'regreplace,
#'remove_action,
#'remove_call_out,
#'remove_input_to,
#'remove_interactive,
#'rename,
#'rename_object,
#'replace_program,
#'restore_object,
#'restore_value,
#'right,
#'rm,
#'rmdir,
#'round,
#'rusage,
#'save_object,
#'save_value,
#'say,
#'search_object,
#'sein,
#'seinem,
#'seinen,
#'seines,
#'send_erq,
// #'send_imp,
#'send_udp,
// #'set_auto_include_string,
#'set_bit,
#'set_buffer_size,
#'set_combine_charset,
#'set_connection_charset,
#'set_driver_hook,
#'set_environment,
#'set_extra_wizinfo,
#'set_extra_wizinfo_size,
#'set_heart_beat,
#'set_is_wizard,
#'set_light,
#'set_limits,
#'set_living_name,
#'set_modify_command,
#'set_next_reset,
#'set_prompt,
#'set_this_object,
#'set_this_player,
#'seteuid,
#'sgn,
#'shadow,
#'short_format_seconds,
#'short_format_vseconds,
#'shorttimestr,
#'shortvtimestr,
#'shout,
#'shutdown,
#'sin,
#'sizeof,
// #'slice_array,
#'snoop,
#'sort_array,
#'space,
#'sprintf,
#'sqrt,
#'sscanf,
#'str2int,
#'string_parser,
#'stringp,
#'strip,
#'strlen,
#'strstr,
#'substr,
// #'swap,
#'symbol_function,
#'symbol_variable,
#'symbolp,
#'sys_log,
#'tail,
#'tan,
#'tell_object,
#'tell_room,
#'terminal_colour,
#'test_bit,
#'this_interactive,
#'this_object,
#'this_player,
#'throw,
#'time,
#'time_to_vtime,
#'timestr,
#'to_array,
#'to_float,
#'to_int,
#'to_object,
#'to_string,
#'touch,
#'trace,
#'traceprefix,
#'transpose_array,
#'trim,
#'typeof,
#'unbound_lambda,
#'unique_array,
#'unmkmapping,
#'unshadow,
#'upper_case,
#'users,
#'utime,
#'vclock,
#'vtime,
#'vtime_to_time,
#'vtimestr,
#'walk_mapping,
#'wem,
#'wen,
#'wer,
#'wessen,
#'widthof,
#'wizardshellp,
#'wizlist_info,
#'wrap,
#'wrap_say,
#'write,
#'write_bytes,
#'write_file,
#'xor_bits,
#'xy2pol,
#',,
#'=,
#'+=,
#'-=,
#'&=,
#'|=,
#'^=,
#'<<=,
#'>>=,
#'>>>=,
#'*=,
#'%=,
#'/=,
#'?,
#'||,
#'&&,
#'|,
#'^,
#'&,
#'==,
#'!=,
#'>,
#'>=,
#'<,
#'<=,
#'<<,
#'>>,
#'>>>,
#'+,
#'-,
#'*,
#'%,
#'/,
#'++,
#'--,
#'negate,
#'!,
#'~,
#'({,
#'([,
#'[,
#'[<,
#'[..],
#'[..<],
#'[<..],
#'[<..<],
#'[..,
#'[<..,
#'({,
#'([,
}) ),
( values =
({
/* --- Integers: --- */
0,
1,
-1,
32,
-32,
40,
-40,
29292929,
-5050211,
__INT_MAX__,
__INT_MIN__,
/* --- Floats: --- */
0.0,
0.5,
-0.5,
55.5,
-55.5,
999999999999999999999999999999999999999999999.99,
-999999999999999999999999999999999999999999999.99,
/* --- Strings: --- */
"",
"foo bar",
"%d %q %T",
"0",
"",
" ",
"_",
"^^^^^^",
"#^# #^#",
" ^",
"^ ",
" - - - - ",
"? ? ? ? ? ? ????? ???? ??",
"<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>",
"\\/ "*32,
" !"*42,
/* --- Objekte: --- */
touch("/obj/fackel"),
touch("/obj/rucksack"),
touch("/obj/rope"),
touch("/secure/master"),
clone_object("/obj/schiff"),
clone_object("/obj/player"),
clone_object("/obj/tisch"),
/* --- Closures: --- */
(: :),
(: (: 1 :) :),
#'sizeof,
#'garbage_collection,
symbol_function("query_long", touch("/i/item")),
lambda( ({'x, 'y}), ({#'?, ({#'>, 'x, 'y}), 'x, 'y}) ),
unbound_lambda( ({'x}), ({#'==, 0, 'x}) ),
/* --- Arrays: --- */
({ }),
({ 0 }),
({ "" }),
({ ({ }) }),
({ ([ ]) }),
allocate(3000),
/* --- Mappings: --- */
([ ]),
([ (: :) ]),
m_allocate(5,5),
mkmapping(allocate(30), allocate(30, ({})), allocate(30, (:1:))),
}) )
); // catch
if(pointerp(values)) {
values += ({0,0,0});
apply((: values[<3] = $1 :), &execute);
apply((: values[<2] = $1 :), &values);
apply((: values[<1] = $1 :), &efuns);
}
while(remove_call_out("execute_file") != -1);
while(remove_call_out("do_replay") != -1);
while(remove_call_out("do_record") != -1);
seteuid(getuid());
}
int execute_file(string file)
{
int line;
string str;
mixed restore;
if(!stringp(file) || file_size(file) <= 0)
{
DEBUG("ERROR: Cannot execute "+file+": Invalid file.");
return 0;
}
DEBUG("Executing: "+file);
for(line = 0; (str=read_file(file, line, 2)) && strlen(str); line += 2)
{
restore = restore_value(str);
rm("/save/crash/LAST_EXEC");
write_file("/save/crash/LAST_EXEC",
save_value( ({file, line}) ));
#ifdef VERBOSE
debug_message("File: "+file+" Line: "+line+"\n");
#endif
debug_message(sprintf("REPLAY: %O %O -> %O %O\n", restore[0], restore[1], efuns[ restore[0] ], map( restore[1], (: values[$1] :) )));
catch(
apply( efuns[ restore[0] ],
map( restore[1], (: values[$1] :) ) )
);
}
return 1;
}
mixed query_values() { return values; }
int generate_file(string file)
{
int i, h, efun_index, * value_index;
if(!stringp(file) || !write_file(file, ""))
{
DEBUG("ERROR: Cannot write file: "+file);
debug_message(wrap("ERROR: Cannot write file: "+file));
seteuid(getuid());
record("/save/crash/"+(mixed2str(utime())-"({,})"));
return 0;
}
DEBUG("Recording: "+file);
for(i = ITERATIONS; i--; )
{
efun_index = random(sizeof(efuns));
value_index = ({});
for(h = random(MAX_ARGS+1); h--; )
{
value_index += ({ random(sizeof(values)) });
}
write_file(file, save_value( ({efun_index, value_index}) ));
}
return 1;
}
void replay(string file)
{
if (!file)
{
file = names[0];
names = names[1..];
}
DEBUG("Stopping all actions and starting REPLAY for: "+file);
reset_all();
execute = ({ #'call_out, "do_replay", DELAY, file, 1 });
// Um this_player etc. loszuwerden einen Reset-Hack ;o)
set_next_reset(DELAY);
}
static void do_replay(string file, int number)
{
if(execute_file(file + right(number, 5, "_0000")))
{
call_out("do_replay", DELAY, file, number+1);
}
else call_out("replay", DELAY, 0);
}
void record(string file)
{
DEBUG("Stopping all actions and starting RECORD for: "+file);
reset_all();
execute = ({ #'call_out, "do_record", DELAY, file, 1 });
// Um this_player etc. loszuwerden einen Reset-Hack ;o)
set_next_reset(DELAY);
}
static void do_record(string file, int number)
{
if(generate_file(file+right(number, 5, "_0000")))
{
call_out("execute_file", DELAY, file+right(number, 5, "_0000"));
call_out("do_record", DELAY*2, file, number+1);
}
}
void reset()
{
// Um this_player etc. loszuwerden einen Reset-Hack ;o)
if(execute)
{
apply(execute[0], execute[1..]);
execute = 0;
}
}
void debug_query(string file, int line)
{
mixed restore;
restore = restore_value(read_file(file, line, 2));
rm("/save/crash/TEMP");
write_file("/save/crash/TEMP",
sprintf("apply( %Q, %Q )", efuns[ restore[0] ],
map(restore[1], (: values[$1] :))));
({this_player()})->more("/save/crash/TEMP");
}
void debug_exec(string file, int line)
{
mixed restore;
restore = restore_value(read_file(file, line, 2));
catch(
apply( efuns[ restore[0] ], map(restore[1], (: values[$1] :)))
);
}
void create()
{
string str;
mixed strs;
reset_all();
str = read_file("/save/crash/LAST_EXEC");
if(str)
{
catch( apply( #'debug_query, restore_value(str) ),
write_file("/save/crash/CRASHERS",
"-"*80 + "\n"
+ read_file("/save/crash/TEMP")+ "\n" ) );
}
#if 0
strs = get_dir("/save/crash/1*");
foreach(str : strs)
{
rm("/save/crash/"+str);
}
#endif
#if 1
call_out("record", 4, "/save/crash/"+(mixed2str(utime())-"({,})"));
#else
names = ({
"/save/crash/crash1"
});
call_out("replay", 4, 0);
#endif
}
/* --- End of file. --- */