Who's Online |
52 user(s) are online ( 39 user(s) are browsing Forums)
Members: 1
Guests: 51
flash,
more...
|
|
|
|
Re: AmiPDF ?
|
|
Home away from home 
|
@Maijestro Check also my old port of VPDF from Morphos (on os4depot) should be fast enough too. Quote: can I instruct AmigaOs4.1 to always open PDF files with RNOPDF as soon as I use them?
In system:prefs/Env-Archive/Sys/ find the one for pdf (can't check now, but this probably def_pdf.info) and change in the tooltypes for binary you need.
|
|
|
|
Re: clib2 vs newlib perfomance issues
|
|
Home away from home 
|
@all Andrea did some good bunch of optimization including not calling check_abort() when not need it, as well as instead of line-buffering usefull buffering, and all f* (fwrite,fputs, etc) based functions now on the same speed level as newlib, same as pure puts(), and all vrptinf/string-formatting based ones also gain some speed , while still not as good as newlib because of this FILE * crap, but at least much better even with this FILE * left from clib2.
|
|
|
|
Re: Snork: New Tracing Tool for AmigaOS 4
|
|
Home away from home 
|
@msteed Design question: Should we allow any format specifier for any argument, or restrict them to a specific set? If we allow any, users could easily use %s for a decimal value and create a mess (though we can already detect invalid strings and limit their size). Allowing all format specifiers feels logical: users can choose what they want—pointers, hex, decimal, strings, etc.—but it requires caution. Alternatively, we could limit specifiers to match the types expected by the patched function. But then, what's the point of setting them if they're fixed to the function's expected types? In that case, we wouldn't need scripting—just a list of functions with the ability to mark unneeded args with "-".
IMHO, we should support the same range of printf-style format specifiers and let users decide what they want to see. What do you think?
Also, I'm unsure whether we should provide an option to enable/disable "task/process" info and "return type," or just always show the task name/address and return type by default and no worry about disabling. What do you think?
|
|
|
|
Re: Snork: New Tracing Tool for AmigaOS 4
|
|
Home away from home 
|
@msteed Quote: I wrote my original comment back before you switched to IPC. Now that you're copying all the arguments anyway, you're right, it's no big deal to wait until the call returns so you can report the return code as well. Snork is getting better and better!
In small tests even without IPC taking result and then print it with arguments works, but yea, after calling original function arguments can easy die, so copy need it..
|
|
|
|
Re: X5000 maybe dying :(
|
|
Home away from home 
|
My x5k/020 one always 74-78 for all the ten years.
|
|
|
|
Re: Snork: New Tracing Tool for AmigaOS 4
|
|
Home away from home 
|
@msteed btw, Quote: It would be nice to be able to see the return value from the patched calls, though that will be a bit more complex as you need to report twice, once before the call and once after.
There are some functions that return 64-bit values too, if you get to the point of reporting on return codes.
But i can just call original function before the log, so to have result as well at the same time, i mean something like:
// Wrapper function for patching Open
static BPTR Patched_Open(struct DOSIFace *Self, CONST_STRPTR name, LONG accessMode) {
BPTR result = 0;
// Call the original function
if (Original_Open) {
result = Original_Open(Self, name, accessMode);
}
// Log arguments and return value
LogPatchedFunction(RETURN_BPTR, result, "IDOS->Open(name='%s', accessMode=%ld)", name ? name : "NULL", accessMode, result);
return result;
}
or for int64 return:
// Wrapper function for patching GetFilePosition
static int64 Patched_GetFilePosition(struct DOSIFace *Self, BPTR fh) {
int64 result = 0;
// Call the original function
if (Original_GetFilePosition) {
result = Original_GetFilePosition(Self, fh);
}
// Log arguments and return value
LogPatchedFunction(RETURN_INT64, result, "IDOS->GetFilePosition(Self=0x%lx, fh=0x%lx)", (uint32)Self, fh);
return result;
}
And log it like:
uint32 VARARGS68K LogPatchedFunction(uint32 returnType, int64 returnValue, const char *format, ...) {
char buffer[512];
va_list args;
va_startlinear(args, format);
APTR data = va_getlinearva(args, APTR);
IExec->RawDoFmt(format, data, NULL, buffer);
if (returnType != RETURN_NONE) {
char returnBuffer[64];
switch (returnType) {
case RETURN_BPTR: {
// Use lower 32 bits for BPTR
int32 value = (int32)(returnValue & 0xFFFFFFFF);
IExec->RawDoFmt(" return=0x%lx\n", &value, NULL, returnBuffer);
break;
}
case RETURN_INT32: {
// Use lower 32 bits for int32
int32 value = (int32)(returnValue & 0xFFFFFFFF);
IExec->RawDoFmt(" return=%ld\n", &value, NULL, returnBuffer);
break;
}
case RETURN_LONG: {
// Use lower 32 bits for LONG
int32 value = (int32)(returnValue & 0xFFFFFFFF);
IExec->RawDoFmt(" return=%ld\n", &value, NULL, returnBuffer);
break;
}
case RETURN_INT64: {
// Use full int64
IExec->RawDoFmt(" return=%lld\n", &returnValue, NULL, returnBuffer);
break;
}
case RETURN_BOOL: {
// Use lower 32 bits for BOOL (normalized to 0 or 1)
int32 value = (int32)(returnValue & 0xFFFFFFFF);
IExec->RawDoFmt(" return=%ld\n", &value, NULL, returnBuffer);
break;
}
default:
returnBuffer[0] = '\0'; // No return value if type is unknown
break;
}
// Append return value to the log
IExec->DebugPrintF("%s%s", buffer, returnBuffer);
} else {
IExec->DebugPrintF("%s", buffer);
}
va_end(args);
return 0;
}
Something of that sort.. So it will be:
IIntuition->SetWindowTitles(Self=0x6FFFFC00, window=0x600A52E0, windowTitle=AmigaShell, screenTitle=0xFFFFFFFF)
IDOS->Open(name='NIL:', accessMode=1005) return=0x183F26EC
IDOS->Open(name='console:', accessMode=1006) return=0x183F2722
IDOS->Open(name='Env:Sys/console.prefs', accessMode=1005) return=0x17FE285C
IDOS->ChangeFilePosition(file=0x188092F2, position=10, offset=-1) return=0
IDOS->GetFilePosition(Self=0x6FFA05D0, fh=0x188092F2) return=0
IDOS->ChangeFilePosition(file=0x1880935E, position=10, offset=-1) return=1
IDOS->GetFilePosition(Self=0x6FFA05D0, fh=0x1880935E) return=10
Edited by kas1e on 2025/7/2 10:52:10 Edited by kas1e on 2025/7/2 11:12:43
|
|
|
|
Re: clib2 vs newlib perfomance issues
|
|
Home away from home 
|
@joerg Quote: You only have 2 options: - Re-implement all clib4 parts from scratch and remove any code from clib4, not only the I/O related parts, which were based on clib2 sources.
Probably only that one to go. Quote: - Throw away the whole junk and restart from scratch, porting a usable C library like newlib, uClib, AROS C library, etc., (a glibc port is next to impossible, a NetBSD libc port way too much work, and my OS4 port of ixemul incomplete and too complicated to install for casual users...), instead.
There only newlib and uClib can be pretended for, because AROS one for sure will have more bugs and there the same no developers mostly (just a few as on os4), and it will be surely full of ifdefs of any sort because of too much different platforms support. We get rid of clib2 just to not have anymore os3/mos ifdefs in which no one use anymore.. But then, its anyway too late to change the route for a complete new rewrite: Andrea already spend a year or two in clib4, so he for sure will start nothing new, he currently have no time to deal with replacing FILE * clib2 crap. From another side, maybe someone on payment basis want to replace FILE * crap in clib4 on proper newlibs one or uClib one ?
|
|
|
|
Re: Snork: New Tracing Tool for AmigaOS 4
|
|
Home away from home 
|
@msteed Also another reason to switch to IPC: if i just call DebugPrintF from patching function, it sometime can overlap output. With IPC it wait for message, and then print it.
|
|
|
|
Re: clib2 vs newlib perfomance issues
|
|
Home away from home 
|
@George Quote: And what if you redirect output to different place. Like some file in RAM: or to NIL:
I doing all tests being in RAM: with >1 redirect, but retested again, and be it redirected to file, to NIL - all the same for original test case : ~19seconds. Then tried "255 128 0" with "n" : 13 seconds Then tried "255 128 0" without "n" : 13 seconds too
|
|
|
|
Re: clib2 vs newlib perfomance issues
|
Posted on: 6/30 16:52
#10
|
Home away from home 
|
@joerg Quote: Just browsed the clib4 sources a bit, and there is still a lot, way too much, crap from clib2 remaining. Very simple example: https://github.com/AmigaLabs/clib4/blob/master/library/stdio/lock.c Just replacing the old, and probably only used for the AmigaOS 1.x-3.x compatibility of clib2, semaphores (based on Forbid()/Permit()!) by OS4 mutexes (based on atomic increment/decrement instructions) should result in a noticeable speed improvement.
Andrea tried to change it all on mutexes : https://github.com/AmigaLabs/clib4/commits/mutexes/ It change a shit :) And i really mean it : no single speed up. Same slow stuff for fwrite(), printf(), puts() and sprintf(). But what were found that if we commented out for example in fwrite.c, check_abourt, lock/unlock, we then gain a bit : like for fwrite it was 13s for test case, and with commented check_abort/lock/unlock start to be 8. Of course nothing mostly, as newlib gives 0.5s on same test, but still something to think about..
|
|
|
|
Re: Snork: New Tracing Tool for AmigaOS 4
|
|
Home away from home 
|
@Joerg Quote: and only a single, pre-allocated memory buffer per patched function isn't enough either: The (patched) functions might be called by different tasks at the same time.
How can i know how much buffers i need then if there can be any amount of diffent tasks calling for example allocvec() (probably lots at the same time) ? @msteed About 64bit issue: i simple create patch_exceptions.c in which patch by hands functions which not fits into generic_patch(). At moment there just 6 from dos.library using mixed 32/64bits args, intuitions ones as far as i see all fits into 32bit way.
|
|
|
|
Re: Snork: New Tracing Tool for AmigaOS 4
|
Posted on: 6/29 10:07
#12
|
Home away from home 
|
@msteed Quote: Typically, your current approach -- doing as little as possible in the patch, and postponing the formatting and output to Snork itself -- is the recommended way to handle such things. But I wonder if in this case your original approach -- doing everything, including the DebugPrintF(), in the patch -- isn't a better idea.
I switched to IPC only because I started losing strings (some had garbage when they shouldn't). Maybe it was a side effect of a bug or something else, but once I began copying strings and tags in the patch and sending them via messages, the bug disappeared. But of course, this means I now have more code to handle, like first copying, then parsing, and overall it’s starting to be a bit too much, but still probably safer... Quote: One drawback to the current approach is that logging the function call is asynchronous to the function call itself- by the time Snork gets the message and outputs the log, the call has already occurred. So if the call crashes due to a bad argument, the crash occurs before the call can be logged (and if the crash takes down the system, the log will never be written).
A bigger problem comes up when you start logging exec.library calls. I presume you allocate memory in the patch to hold all the copies of arguments, and memory allocation can break a Forbid() that might be in effect when Exec is called. It looks like that can be prevented by using the AVT_Wait, FALSE tag with AllocVecTags().
Yes, for copying strings and tags, I use AllocVecTags, and currently my GenericPatch function and SendIPCMessage function look like this: Patching function:
void GenericPatch(struct Interface *Self, int watchIndex, uint32 args[MAX_PARAMS]) {
if (watchIndex < 0 || watchIndex >= watchCount || !watches[watchIndex].origFunc ||
!watches[watchIndex].iface || Self != watches[watchIndex].iface) {
IExec->DebugPrintF("Invalid watchIndex or interface mismatch: watchIndex=%d\n", watchIndex);
return;
}
struct Watch *watch = &watches[watchIndex];
// Prepare parameter types for IPC message
char paramTypes[MAX_PARAMS] = {0};
for (int i = 0; i < watch->paramCount && i < MAX_PARAMS; i++) {
paramTypes[i] = watch->params[i].type;
}
// Handle process filter and task info
if (showProcessName || showTaskInfo) {
char taskName[256];
struct Process *proc = (struct Process *)IExec->FindTask(NULL);
Get_Name(taskName, sizeof(taskName), proc);
if (!showProcessName || strcmp(taskName, showProcessName) == 0)
SendIPCMessage(watchIndex, args, watch->libName, watch->funcName, paramTypes,
showTaskInfo ? taskName : NULL, showTaskInfo ? (uint32)proc : 0);
} else {
SendIPCMessage(watchIndex, args, watch->libName, watch->funcName, paramTypes, NULL, 0);
}
// Call the original function with the appropriate number of parameters
typedef void (*FuncPtr)(struct Interface *, ...);
FuncPtr origFunc = (FuncPtr)watch->origFunc;
switch (watch->paramCount) {
case 0: origFunc(Self); break;
case 1: origFunc(Self, args[0]); break;
case 2: origFunc(Self, args[0], args[1]); break;
case 3: origFunc(Self, args[0], args[1], args[2]); break;
case 4: origFunc(Self, args[0], args[1], args[2], args[3]); break;
case 5: origFunc(Self, args[0], args[1], args[2], args[3], args[4]); break;
case 6: origFunc(Self, args[0], args[1], args[2], args[3], args[4], args[5]); break;
case 7: origFunc(Self, args[0], args[1], args[2], args[3], args[4], args[5], args[6]); break;
case 8: origFunc(Self, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]); break;
case 9: origFunc(Self, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8]); break;
case 10: origFunc(Self, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9]); break;
case 11: origFunc(Self, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9], args[10]); break;
default:
IExec->DebugPrintF("Debug: Unsupported parameter count %ld\n", watch->paramCount);
break;
}
}
SendIPCMessage():
void SendIPCMessage(int watchIndex, uint32 args[MAX_PARAMS], const char *libName, const char *funcName, const char paramTypes[MAX_PARAMS], const char *taskName, uint32 taskAddr) {
if (!ipcPort) {
IExec->DebugPrintF("IPC port not initialized!\n");
return;
}
struct IPCMessage *ipcMsg = IExec->AllocVecTags(sizeof(struct IPCMessage),
AVT_Type, MEMF_PRIVATE,
AVT_ClearWithValue, 0,
TAG_END);
if (!ipcMsg) {
IExec->DebugPrintF("Failed to allocate IPC message!\n");
return;
}
ipcMsg->watchIndex = watchIndex;
IExec->CopyMem(args, ipcMsg->args, sizeof(ipcMsg->args));
IUtility->Strlcpy(ipcMsg->libName, libName, sizeof(ipcMsg->libName));
IUtility->Strlcpy(ipcMsg->funcName, funcName, sizeof(ipcMsg->funcName));
IExec->CopyMem(paramTypes, ipcMsg->paramTypes, MAX_PARAMS);
// Set task name and address only if taskName is provided
if (taskName) {
IUtility->Strlcpy(ipcMsg->taskName, taskName, sizeof(ipcMsg->taskName));
ipcMsg->taskAddr = taskAddr;
} else {
ipcMsg->taskName[0] = '\0'; // Set to empty string (equivalent to NULL)
ipcMsg->taskAddr = 0;
}
// Copy string parameters
ipcMsg->stringParamCount = 0;
for (int i = 0; i < MAX_PARAMS && paramTypes[i] && ipcMsg->stringParamCount < MAX_STRING_PARAMS; i++) {
if (paramTypes[i] == 's') {
const char *str = (const char *)args[i];
if (str) {
IUtility->Strlcpy(ipcMsg->stringParams[ipcMsg->stringParamCount], str, MAX_STRING_LENGTH);
ipcMsg->args[i] = (uint32)ipcMsg->stringParams[ipcMsg->stringParamCount];
ipcMsg->stringParamCount++;
}
}
}
// Copy tag lists
ipcMsg->tagItemCount = 0;
ipcMsg->tagStringParamCount = 0;
for (int i = 0; i < MAX_PARAMS && paramTypes[i]; i++) {
if (paramTypes[i] == 't') {
struct TagItem *tags = (struct TagItem *)args[i];
if (tags) {
int tagCount = 0;
int moreCount = 0;
while (tags[tagCount].ti_Tag != TAG_END && tags[tagCount].ti_Tag != TAG_DONE &&
tagCount < MAX_TAG_ITEMS && ipcMsg->tagItemCount < MAX_TAG_ITEMS &&
moreCount < 10) {
if (tags[tagCount].ti_Tag == TAG_IGNORE) {
tagCount++;
continue;
}
if (tags[tagCount].ti_Tag == TAG_MORE) {
if (tags[tagCount].ti_Data && moreCount < 10) {
tags = (struct TagItem *)tags[tagCount].ti_Data;
tagCount = 0;
moreCount++;
continue;
}
break;
}
if (tags[tagCount].ti_Tag == TAG_SKIP) {
uint32 skipCount = tags[tagCount].ti_Data;
tagCount += skipCount + 1;
if (tagCount >= MAX_TAG_ITEMS) break;
continue;
}
// Check if the tag is a string type
const struct TagInfo *info = findTagInfoForTag(tags[tagCount].ti_Tag, libName);
if (info && info->Type == 's' && tags[tagCount].ti_Data &&
ipcMsg->tagStringParamCount < MAX_TAG_STRING_PARAMS) {
// Copy string to tagStringParams
IUtility->Strlcpy(ipcMsg->tagStringParams[ipcMsg->tagStringParamCount],
(const char *)tags[tagCount].ti_Data, MAX_STRING_LENGTH);
ipcMsg->tagItems[ipcMsg->tagItemCount].ti_Tag = tags[tagCount].ti_Tag;
ipcMsg->tagItems[ipcMsg->tagItemCount].ti_Data = (uint32)ipcMsg->tagStringParams[ipcMsg->tagStringParamCount];
ipcMsg->tagStringParamCount++;
} else {
// Copy tag as-is
ipcMsg->tagItems[ipcMsg->tagItemCount] = tags[tagCount];
}
ipcMsg->tagItemCount++;
tagCount++;
if (ipcMsg->tagItemCount >= MAX_TAG_ITEMS) break;
}
if (ipcMsg->tagItemCount > 0) {
if (ipcMsg->tagItemCount < MAX_TAG_ITEMS) {
ipcMsg->tagItems[ipcMsg->tagItemCount].ti_Tag = TAG_END;
ipcMsg->tagItemCount++;
}
ipcMsg->args[i] = (uint32)ipcMsg->tagItems;
}
}
}
}
ipcMsg->msg.mn_Node.ln_Type = NT_MESSAGE;
ipcMsg->msg.mn_Length = sizeof(struct IPCMessage);
ipcMsg->msg.mn_ReplyPort = NULL;
IExec->PutMsg(ipcPort, (struct Message *)ipcMsg);
}
And then, after I copy it all, I have to reparse and print it, so it's like double the work... But for now I'm facing another problem: 64-bit arguments for some dos.library functions. The issue is: Currently, all my patching code, wrappers for functions, etc., are using uint32/int32. That's everywhere, and all arguments are treated like this too (bytes, words, etc., are treated as longs, so int32, no problems there; strings and pointers fit into int32 as well). But then, dos.library has six functions that allow some arguments to be int64:
watch=dos,ChangeFilePosition,file=%lp,position=%ld,offset=%ld // file (BPTR), position (int64), offset (int32)
watch=dos,ChangeFileSize,fh=%lp,pos=%ld,mode=%ld // fh (BPTR), pos (int64), mode (int32)
watch=dos,DoPkt64,sendport=%lp,type=%ld,arg1=%ld,arg2=%ld,arg3=%ld,arg4=%ld,arg5=%ld // sendport (struct MsgPort *), type (int32), arg1 (int32), arg2 (int64), arg3 (int32), arg4 (int32), arg5 (int64)
watch=dos,PRIVATEDoPkt64,sendport=%lp,type=%ld,arg1=%ld,arg2=%ld,arg3=%ld,arg4=%ld,arg5=%ld // sendport (struct MsgPort *), type (int32), arg1 (int32), arg2 (int64), arg3 (int32), arg4 (int32), arg5 (int64)
watch=dos,LockRecord,fh=%lp,offset=%ld,length=%ld,mode=%ld,timeout=%ld // fh (BPTR), offset (int64), length (int64), mode (uint32), timeout (uint32)
watch=dos,UnLockRecord,fh=%lp,offset=%ld,length=%ld // fh (BPTR), offset (int64), length (int64)
Now, if I patch those functions with my generic patch, which expects all arguments to be int32, I'll just get bugs because an int64 takes up two int32 slots, causing memory shifts, crashes, and other issues. I see that I need to handle int64 as two int32s in all cases, but that means changing GenericPatch, SendIPCMessage, and the logger to always skip two int32s when encountering an int64, and it’s starting to feel like a mess. If I change everything in the code (wrappers, GenericPatch, etc.) to use int64, I’ll run into problems with int32 arguments for the same reasons. So, for now, I’m thinking about how to solve this one...
|
|
|
|
Re: DumbPad v03
|
Posted on: 6/28 17:57
#13
|
Home away from home 
|
@Maijestro D&D were added in v0.2, check release notes :) And yeah, i working on, just not ready for next beta
|
|
|
|
Re: Introducing the Rear Window blog
|
|
Home away from home 
|
@trixie Quote: Now on the Rear Window blog, my personal manifesto for the summer. Enjoy reading!
That's how I think for years, keeping me away from being disappointed by anything. We use what we already have, and if there are updates - cool, if not, I know what I do and with what. Waiting for anything from leaders or hoping for any promises is the way to the trap. Let them do what they think and can do, but it shouldn't reflect on you in any case.
|
|
|
|
Re: Snork: New Tracing Tool for AmigaOS 4
|
Posted on: 6/27 19:26
#15
|
Home away from home 
|
@joerg Quote: to add support for international chars, for example the default ISO 8859-15 ones. Unprintable control chars on AmigaOS, in any supported 8 bit charset, are only 0-31 and 127-159.
Good catch , thanks ! Btw, what is the best way to follow if we need to parse data in a patch ? At fist, i just doing simple parsing inside of the patch, and then print it before calling original function. Then after , i add some simple IPC via PutMsg() (from patch) /GetMsg() (in main). At first i tried to simple send what i have to the main, but find out that strings and other data simple died offten (because patch is done, memory changes, but i tried to handle it in the main which is point on whatever else). Because of that i had to copy in the patch before putmsg whole set : libname, functionname, doing findtaks(null), copy strings and tags, and then send this instead. For copy i use IExec->CopyMem/IUtility->Strlcpy (that for libname, funcname and strings), and for tags i simple do simple loop copy. The question i had now , is it correct way of doing things. I mean, for speed and for being correct at all. As i doing dynamic copy for each patching function, i do not need locks and mutexes, but not sure shouldn't i somehow protect it or not ?
Edited by kas1e on 2025/6/27 19:58:44
|
|
|
|
Re: Snork: New Tracing Tool for AmigaOS 4
|
|
Home away from home 
|
@msteed Quote: For dealing with buggy pointers to something that's not a NUL-delimited string, I was thinking of something simple, like limiting the length that's printed to something reasonable, so you won't potentially get thousands of random characters until it happens to encounter a NUL.
In addition, you could filter the string to turn unprintable characters into periods (or some other character), so you won't possibly print an escape code or control character that interferes with the display of the snooped data. That would require making a copy of the string, since you don't want to alter the original.
For now i limited string to 256 chars, + checking on null and 0xffffffff, + checked on format characters so to printf them as it, without taking as formatting symbols, cheking on fancy characters and replace them to (so printable ASCII character only) , etc. So before show the string i just do this:
// Create a safe, quoted string representation for logging
void safe_string_representation(const char *input, char *output, size_t output_size) {
if (!input) {
strncpy(output, "NULL", output_size - 1);
output[output_size - 1] = '\0';
return;
}
if (input == (CONST_STRPTR)0xFFFFFFFF) {
strncpy(output, "0xFFFFFFFF", output_size - 1);
output[output_size - 1] = '\0';
return;
}
size_t j = 0;
if (j < output_size - 1) {
output[j++] = '"';
}
for (size_t i = 0; input[i] && j < output_size - 2; i++) {
if (input[i] == '%') {
if (j < output_size - 3) {
output[j++] = '%';
output[j++] = '%';
}
} else if (input[i] == '"' || input[i] == '\\') {
if (j < output_size - 3) {
output[j++] = '\\';
output[j++] = input[i];
}
} else if (input[i] >= 32 && input[i] <= 126) {
output[j++] = input[i];
} else {
if (j < output_size - 3) {
output[j++] = '?';
}
}
}
if (j < output_size - 1) {
output[j++] = '"';
}
output[j] = '\0';
if (j >= output_size - 1) {
output[output_size - 1] = '\0';
}
}
And then :
char safe_str[256];
safe_string_representation((const char *)tag->ti_Data, safe_str, sizeof(safe_str));
IExec->DebugPrintF(" %s = %s\n", info->Name, safe_str);
Should be enough imho even without IExec->TypeOfMem() ?
|
|
|
|
Re: Snork: New Tracing Tool for AmigaOS 4
|
Posted on: 6/26 22:07
#17
|
Home away from home 
|
@all Thanks for suggestions ! @Javier Quote: Just when you CTRL+C Snork just show a simple "Snork ended" on Shell/CLI (and maybe to serial outoput too).
Added, now it says start/end on both serial and console. @msteed Quote: It would also be nice if it showed the name of the program making the call like OS4 Snoopy does, so you can see which calls are made by the program being debugged
Done, now output looks like this (some simple trace of few dos functions):
...
Task/Process: Workbench (0x62E0AB30) --> IDOS->Open(name="LOCALE:Catalogs/english_UTF-8/Sys/libs.catalog",accessMode=1005)
Task/Process: Workbench (0x62E0AB30) --> IDOS->AllocDosObject(type=0,tags=0x00000000)
Task/Process: Workbench (0x62E0AB30) --> IDOS->PRIVATEDoPkt32(port=0x6FDF5450,action=1005,arg1=411674462,arg2=415205712,arg3=409172995,arg4=0,arg5=409172995,arg6=0,arg7=0)
Task/Process: Workbench (0x62E0AB30) --> IDOS->PRIVATEDoPkt32(port=0x6FDF5450,action=1005,arg1=411674462,arg2=412428724,arg3=409172995,arg4=0,arg5=409172995,arg6=0,arg7=0)
Task/Process: Workbench (0x62E0AB30) --> IDOS->FreeDosObject(type=0,ptr=0x62269D78)
Task/Process: Workbench (0x62E0AB30) --> IDOS->IoErr()
Task/Process: Workbench (0x62E0AB30) --> IDOS->Open(name="LOCALE:Catalogs/english_US_ASCII/Sys/libs.catalog",accessMode=1005)
Task/Process: Workbench (0x62E0AB30) --> IDOS->AllocDosObject(type=0,tags=0x00000000)
Task/Process: Workbench (0x62E0AB30) --> IDOS->PRIVATEDoPkt32(port=0x6FDF5450,action=1005,arg1=411674462,arg2=415205712,arg3=409172995,arg4=0,arg5=409172995,arg6=0,arg7=0)
Task/Process: Workbench (0x62E0AB30) --> IDOS->PRIVATEDoPkt32(port=0x6FDF5450,action=1005,arg1=411674462,arg2=412428724,arg3=409172995,arg4=0,arg5=409172995,arg6=0,arg7=0)
Task/Process: Workbench (0x62E0AB30) --> IDOS->FreeDosObject(type=0,ptr=0x62269D78)
Task/Process: Workbench (0x62E0AB30) --> IDOS->IoErr()
Task/Process: dopus_clock (0x61F8C1F0) --> IDOS->DateStamp(date=0x61A2ED44)
Task/Process: ELF Collector (0x6FDB57A0) --> IDOS->Delay(timeout=25)
Task/Process: ELF Collector (0x6FDB57A0) --> IDOS->Delay(timeout=25)
Task/Process: dopus_clock (0x61F8C1F0) --> IDOS->DateStamp(date=0x61A2ED44)
Task/Process: ELF Collector (0x6FDB57A0) --> IDOS->Delay(timeout=25)
Task/Process: ELF Collector (0x6FDB57A0) --> IDOS->Delay(timeout=25)
Task/Process: dopus_clock (0x61F8C1F0) --> IDOS->DateStamp(date=0x61A2ED44)
Task/Process: ELF Collector (0x6FDB57A0) --> IDOS->Delay(timeout=25)
Task/Process: ELF Collector (0x6FDB57A0) --> IDOS->Delay(timeout=25)
Task/Process: dopus_clock (0x61F8C1F0) --> IDOS->DateStamp(date=0x61A2ED44)
Task/Process: ELF Collector (0x6FDB57A0) --> IDOS->Delay(timeout=25)
Task/Process: ELF Collector (0x6FDB57A0) --> IDOS->Delay(timeout=25)
Task/Process: dopus_clock (0x61F8C1F0) --> IDOS->DateStamp(date=0x61A2ED44)
Task/Process: ELF Collector (0x6FDB57A0) --> IDOS->Delay(timeout=25)
Task/Process: snork (0x62E0A9B0) --> IDOS->Output()
Task/Process: snork (0x62E0A9B0) --> IDOS->IoErr()
Task/Process: snork (0x62E0A9B0) --> IDOS->IoErr()
Task/Process: snork (0x62E0A9B0) --> IDOS->Write(file=0x1839CFA8,buffer=0x60671000,length=51)
...
Also added 3 arguments now :
IDOS->Printf("Usage: %s [--help] [--script=<filename>] [--taskinfo=<yes|no>] [--show=<process name>]\n", argv[0]);
IDOS->Printf(" --help Show this help message and exit\n");
IDOS->Printf(" --script=<filename> Script file to parse (must end with .snork, default: main.snork)\n");
IDOS->Printf(" --taskinfo=<yes|no> Show task/process info (default: yes)\n");
IDOS->Printf(" --show=<process name> Log only for specified process name (default: all processes)\n");
IDOS->Printf("Note: For process names with spaces, quote the entire argument, e.g., --show=\"ELF Collector\"\n");
So you can 1), enable/disable task names, 2) use any script you wish (default main.snork), 3) have output from any task you want. For string handling at minimum will do as Joerg says with IExec->TypeOfMem() , better than nothing (for now). ps. Btw, added also new keyword for template %t - so you can output tags instead of pointer, and then output will looks like this:
Snork started!
Task/Process: WinFrame 1 Process (0x62907070) --> IDOS->Open(name="NIL:",accessMode=1005)
Task/Process: WinFrame 1 Process (0x62907070) --> IDOS->CreateNewProc()
tags: (tags=0x61A58EE0)
NP_Seglist = 0x1BFCF735
NP_WindowPtr = 0x0
NP_Error = 0x0
NP_Path = 0x0
NP_FreeSeglist = FALSE
NP_Input = 0x0
NP_Output = 0x0
NP_CloseInput = FALSE
NP_CloseOutput = FALSE
NP_CurrentDir = 0x0
NP_ProgramDir = 0x0
NP_Cli = TRUE
SYS_Asynch = TRUE
SYS_Input = 0x0
SYS_Output = 0x0
SYS_Error = 0x0
SYS_UserShell = TRUE
NP_Path = 0x189539AC
NP_CopyVars = TRUE
NP_Name = "DuplicateShell"
NP_Priority = 0
NP_StackSize = 65536
NP_ConsolePort = 0x0
Processed 23 tags
Task/Process: DuplicateShell (0x62082830) --> IDOS->Open(name="console:",accessMode=1006)
Task/Process: NewShell (0x62082830) --> IDOS->CreateNewProc()
tags: (tags=0x61A58100)
NP_Seglist = 0x1BFCF735
NP_FreeSeglist = FALSE
NP_StackSize = 65528
NP_Cli = TRUE
NP_ProgramDir = 0x0
NP_CloseInput = FALSE
NP_CloseOutput = FALSE
NP_CurrentDir = 0x0
NP_Input = 0x0
NP_Output = 0x0
NP_CopyVars = TRUE
NP_Name = "Shell Process"
Processed 12 tags
For that had to create a base of the tags for every library (as they can cross, in hope they will not). And i also move all stuff to IPC now, i.e. in generic_patch just now send the message and call to original, all loging/parsing happens in main's handleinput.
|
|
|
|
Re: Tracing of callhookpkt()/callhook() and varargs
|
|
Home away from home 
|
@joerg Quote: The varargs functions simply call the non-varargs ones, not only in dos but in all OS4 libraries.
Looks so indeed. That what happens when i patch all 3 : CreateNewProc(), CreateNewProcTagList() and CreateNewProcTags(...) and simple call CreateNewProcTags with 5 tags:
IDOS->CreateNewProcTags(...)
NP_Entry = 0x7FFB2500
NP_Name = amiga1200
NP_FreeSeglist = 0
NP_CloseOutput = 1
NP_Child = 1
IDOS->CreateNewProcTagList(tags=0x5FDADCC8)
IDOS->CreateNewProc(tags=0x5FDADCC8)
CreateNewProcTagList() is the one i called from patched CreateNewProcTags, and CreateNewProc() seems the one called from TagList one. Because if i simple replace in varargs one to call at end CreateNewProc() one instead of TagList one, then output is:
IDOS->CreateNewProcTags(...)
NP_Entry = 0x7FFB2500
NP_Name = amiga1200
NP_FreeSeglist = 0
NP_CloseOutput = 1
NP_Child = 1
IDOS->CreateNewProc(tags=0x5FD9CCC8)
Btw, what about Printf(), DebugPrintF(), etc ? They all call VSNPrintf() in end ? But then, user want to know exactly what he call imho, even if after it go over other function (so maybe it need to be patched, logged, and giving control to the one which called for real after) ? Because seeing in log bunch of VSPirntf() while you call for example pure Printf() or DebugPrintF() will make it looks wrong imho.
Edited by kas1e on 2025/6/25 6:48:42
|
|
|
|
Re: Tracing of callhookpkt()/callhook() and varargs
|
Posted on: 6/24 11:31
#19
|
Home away from home 
|
@all Can anyone bring some ideas how to patch/trace varargs based functions (such as Tags(...), format strings ones, and those specific rare cases where varargs used but not fits in first 2 categries) ? I tried to patch for test CreateNewProcTags(...), and can't make it work as it, because it takes (...), i then reparce them via va_* functions, and then i need to call original version, which didn't take (...) anymore but args => crash. I.e:
// Wrapper function for patching CreateNewProcTags
static void VARARGS68K Patched_CreateNewProcTags(struct DOSIFace *Self, ...) {
va_list args;
va_startlinear(args, Self);
struct TagItem *tags = va_getlinearva(args, struct TagItem *);
Original_CreateNewProcTags(Self, tags); // ! Can't! Expected (...)
va_end(args);
}
That one crashes of course.. I can replace of course call to original by CreateNewProcTagList(tags), and it then works, but then it isn't much "tracer", but "replacer" :) All i need is to simple grab the (...), parce all tags from till TAG_DONE/TAG_END found, print, and return all of them back to original, but of course i can't via a way i do it. But maybe there some other ways how i can do so ? Like some varargs forwarding or so ?
Edited by kas1e on 2025/6/24 11:59:07
|
|
|
|
Re: NULL, 0xFFFFFFFF and Exec: Real vs QEMU
|
|
Home away from home 
|
@jabirulo Quote: It's on the autodoc for such "strange" values.
Yeah, read it all yesterday already. Is it only SetWindowTitles allowing -1 or there are more ? I see NULL is acceptable offten, but -1 ?
|
|
|
|