>You need to make the trace collection function not depend on any globals and pass the process id to trace via a message
That won’t really work, by the time you get message, the task your monitoring is at different location in its program. So you will probably get poor stack traces.
(NutsAboutAmiga)
Basilisk II for AmigaOS4 AmigaInputAnywhere Excalibur and other tools and apps.
@All Ok, so, i probably do something very wrong (as i simply crashes now once reached the code sending the message from patched CallHookPkt), but there it is:
At top (out of any functions, so kind of global still?) create:
So with that, once i reach first time patched CallHookPkt, and it send the message i simple crashes. If i add (for sake of tests) some Delay(5) in this part, i can see that my ProcessID always 155, and that seems whole messaging combo didn't works as expected. But i surely miss something trivial..
I don’t know why you want your code to have more likely bugs.
Well, I haven't started doing it yet, but it's the next best thing to a union.
Quote:
now image you pointer is NULL, first is offset 0, 2en is offset 4, now image if member is 2en.
Obviously to avoid that the "ptr" would be tested as being valid first.
Quote:
The only thing you need to do, to screw up your code, is add a new member at the beginning of your struct, and your code breaks.
A new member can't be added because a Task must remain static. Process can be extended but a Process is always a subclass of Task and the Task superclass cannot be changed. In any case this works because the first member of a Process is a Task. The same way it can work if it was passed to a function expecting a Node and the Node field specified.
At least for 99% of the OS4 code doesn't use direct function calls but always goes through the interface of the library.
That would be rather inefficient and slow it down. In particular Intuition used to call internal functions. I used to peek and poke a lot. Being C it stacked a lot of parameters and using internal functions avoided stacking registers. But if OS4 doesn't call internally the same way then it will incur the double direction of jump table calls OS4 gets criticised for.
Quote:
On OS4 VARARGS68K and va_(get|start)linearva does that. It was required for compatibility to emulated m68k code passing the arguments on the stack and it's used for example for all of the varargs TagItem functions.
I noticed those VARARGSS68K but I didn't see why they were necessary. Any emulation of 68K var args is internally to the emulators so shouldn't need to be exposed. Aside from that varargs are a common function practice in portable C code that can run on x86 or PPC and others so varargs never looked 68K exclusive to me.
In other words, even if we know that we can do something because of jump table and interfaces, we still dont know for now how to detect what function of the stacktraced library (at least library) were called :(
Unless the function prologue is on the stack frame no it cannot be known exactly. An exact address in the interface can be matched to that offset easily and a name found. Well, after walking a whole list, which is one way about it.
With an offset not matching an exact function jump, then it's harder and looking for closest in jump table. So bit of work. And it still can be a best guess to pin point it.
Quote:
So with that, once i reach first time patched CallHookPkt, and it send the message i simple crashes. If i add (for sake of tests) some Delay(5) in this part, i can see that my ProcessID always 155, and that seems whole messaging combo didn't works as expected. But i surely miss something trivial..
You've done two funny things and perhaps even three.
Aside from the allocation overhead this would be fine, except for one thing. You've set the reply port as your main port. When your main task replies it back it will be endlessly sent back to your main task!
You need a local port to give it a reply port. The info will be in the SDK. Now you can avoid creating a reply port by using ASOPORT_Signal in the tags. One tag you will need to give and you can just supply a signal. You will need a signal. You don't know if all signals are taken so it might break getting another one. Just avoid it and use SIGF_SINGLE. Free for single use.
@Hypex Thanks for the answer, but I need some clarification:
You say i need local port to give a reply port, but I can avoid it, while can't (as i need it), but instead create it by some other than AllocSysObjectTags way as it have “allocation overhead” ? Or what you mean exactly ?
As i understand it, you still suggest to use for AllocSysObjectTags also ASOPORT_Signal when allocation for message, and this mean i can not create second reply port, and handle it without ? (sounds strange!) But what about “allocation overhead” which you mention, then if AllocSysObjectTags() still should be used for ?
Then i tried like this, and while it didn't crash, it didn't work, it just stuck on the first receive.
struct ProcessIDMessage *message = (struct ProcessIDMessage *)IExec->AllocSysObjectTags(ASOT_MESSAGE, ASOMSG_Size, sizeof(struct ProcessIDMessage), ASOPORT_AllocSig, FALSE, ASOPORT_Signal, SIGB_SINGLE, TAG_DONE);
if (message != NULL) {
message->processID = process->pr_ProcessID; // Get the process ID here
}
IExec->SetSignal(0, SIGF_SINGLE);
IExec->PutMsg(mainPort, (struct Message *)message);
IExec->Wait(SIGF_SINGLE);
The main()'s code is not changed, of course. Hope you wasn't mean that i need to change that too and fully rewrite it to handle new sort of signal without receiving a reply ?:)
And where is FreeSysObject() in your example ? Should't it be right after Wait(SIGF_SINGLE) ?
ps. Never touched anything with all this messages, but after reading wiki were under impression that for communicate between 2 tasks i need one single port , in which i do this communication by messages, not that i need different port for each messages which should be send from one task to another..
Edited by kas1e on 2024/5/7 19:43:13 Edited by kas1e on 2024/5/7 19:45:56
@kas1e Your hook function runs in the caller task while your main runs in your own task. You have to stop the caller after messaging the main task until the main task finishes collecting the stack trace otherwise the caller continues and may exit or will not be at the place you want to trace. There are several ways to do that: 1. Reply with a message, the caller needs a message port for that in addition to the main's message port where you call it. 2. Wait for a signal, then the main task needs to raise the signal (don't know what's the corect term for that in AmigaOS) once finished to make the caller continue. 3. or maybe in the hooked function call suspend self after sending the message and from the main task call restart for the process ID that was passed in the message, then you won't need either a reply message port nor a signal.
. or maybe in the hooked function call suspend self after sending the message and from the main task call restart for the process ID that was passed in the message, then you won't need either a reply message port nor a signal.
That looks like the way to go , and sounds more easy!
You say i need local port to give a reply port, but I can avoid it, while can't (as i need it), but instead create it by some other than AllocSysObjectTags way as it have “allocation overhead” ? Or what you mean exactly ?
Sorry was typing reply in a rush before bed time.
Okay so by local port I meant a port local to your patched function. You don't necessarily need a port, it's just the messaging system is designed in a two way fashion, so a message is sent to a task and that task replies the message back when it has finished.
The port doesn't need to be a normal port either where a message is sent back to. A message can be bounced back as a signal or even trigger a software interrupt as you've seen.
By “allocation overhead” I mean needing to allocate memory on the fly. So by comparison local variables are less overhead as they just get reserved on the stack and avoid an API call. I'm most likely just being pedantic as I would avoid any system calls allocating resources inside a debug routine. Given OS4 has the slab allocator it's probably not such a big deal.
Quote:
As i understand it, you still suggest to use for AllocSysObjectTags also ASOPORT_Signal when allocation for message, and this mean i can not create second reply port, and handle it without ? (sounds strange!) But what about “allocation overhead” which you mention, then if AllocSysObjectTags() still should be used for ?
Sorry, I see I've confused you. In this case you allocate a message. But it needs a reply port allocated as well. The reply port can just be specified as a signal using the ASOPORT_Signal tags as you have set up. But you will need to allocate a port with ASOT_PORT type and those tags.
However, since you are only messaging your own task, you can just skip the reply port all together. It can be set to zero in the message, in which case it will just mark the message as free when replied back. In this case, you can simply skip another port, and just signal back directly. When your main task has finished with the message just grab the process ID from the ProcessIDMessage and signal it back.
Quote:
The main()'s code is not changed, of course. Hope you wasn't mean that i need to change that too and fully rewrite it to handle new sort of signal without receiving a reply ?:)
It will need to bounce it back. You can use the ReplyMsg() mechanism which will need a port attached. Or just simply signal back with a SIGF_SINGLE in this case. That might be best in this case. It avoids setting up a port and you can just use a preset signal.
Quote:
And where is FreeSysObject() in your example ? Should't it be right after Wait(SIGF_SINGLE) ?
Oh yes it should. I just focussed on the message and waiting for signal to go ahead and continue.
Quote:
ps. Never touched anything with all this messages, but after reading wiki were under impression that for communicate between 2 tasks i need one single port , in which i do this communication by messages, not that i need different port for each messages which should be send from one task to another..
In the common case two ports will be involved. For source and destination. Source sends to destination, which then replies it back to source when finished.
So the simplest way if avoiding the reply port would be this:
So the simplest way if avoiding the reply port would be this:
Simple crashed right after i got "mainport received: ProcessID = 191" and code do "IExec->Signal((struct Task *)receivedMessage->processID, SIGF_SINGLE);".
I.e. processID messages from patched hook fine first time, we receive it, print the value, but then, when we need to answer we crash.
if (message != NULL) {
message->processID = process->pr_ProcessID; // Get the process ID here
}
IExec->SetSignal(0, SIGF_SINGLE);
IExec->PutMsg(mainPort, (struct Message *)message);
IExec->Wait(SIGF_SINGLE);
IExec->FreeSysObject(ASOT_MESSAGE, message);
@kas1e Stop allocating anything in your patched function, your patch code is running in a foreign tasks and you don't know in which state it is. Allocating anything, memory, ports, messages, etc., can for example break Forbid() since the memory, page, etc. allocator may have to wait for exclusive access with a mutex.
As long as you only want to debug a single task messages aren't required, sending signals is enough. In the patch you can't allocate signals, for example fails if all are used already, use SIGF_SINGLE instead. For the main task you can allocate a signal.
I noticed those VARARGSS68K but I didn't see why they were necessary. Any emulation of 68K var args is internally to the emulators so shouldn't need to be exposed.
Would have required 2 different implementations for all varagrs TagItem functions: One which can be called from PPC native code and a different one which can be called from emulated m68k code.
Quote:
Aside from that varargs are a common function practice in portable C code that can run on x86 or PPC and others so varargs never looked 68K exclusive to me.
The C parts are common, but the implementation of varargs is CPU specific. IIRC on m68k everything is put on the stack, but on PPC the first 8 integer, FPU and AltiVec arguments are passed in registers and only if there are more the stack is used. VARARGS68K doesn't use the standard PPC varagrs method but only the stack, exactly like on m68k, and a single function can be called form both PPC native and emulated m68k code.
@joerg I am completely looses now :( Aren't we tried to use messages/signals whatever for exactly purposes to send ProcessID from the patch to the main ? In your example, processID aren't transferred, which was the whole purpose of last 2 pages :)
Aren't with your example we simply return to globals again ? Because i had to define patched_task and main_task as globals out of the functions, and we then back to the same as before (process_to_inspect = process); with only one difference that we add signals for wait and send now around.
Aren't we tried to use messages/signals whatever for exactly purposes to send ProcessID from the patch to the main ? In your example, processID aren't transferred, which was the whole purpose of last 2 pages :)
Yes, but I don't understand why. In your patch you are checking the task name or ProcessID and only do something for this single task. For a single task using signals is enough. Sending messages with the ProcessID to the main task would only be required if you want to debug different tasks at the same time.
@joerg The problem was that for single invoking of CallHookPkt from my test binary, we have 150 CallHookPkts in the system. So, idea was to catch all those 150 CallHookPkts, and made a stack traces out of them all (to see, from where all they come).
So, if we will now just stacktrace my single task, we will have just one stack trace for my process. While need to have all stack traces for all CallHookPkts : with global set as before for 150 CallHookPkts i only have 12 stacktraces. All i want is to know from where each CallHookPkts come , all 150.
So i need something like - 1 CallHookPkt happens - 1 stack trace provides. For this purposes i probabaly must to do suspend/restart of tasks, but again, with globals and without messaging this wasn't enough (12 stack traces for 150 callhookpkts only).
Now trying to find a easy way to catch every callhookpkt (but only when my binary invoking) and made a stacktrace of any of them.
with global set as before for 150 CallHookPkts i only have 12 stacktraces
Because you didn't stop the patched task and wait until the main() task is done with the stack trace. It can be done in different ways, sending messages, or SupendTask() in the patch and RestartTask() it in the main task after the stack trace is done, but using signals is the easiest way.
Quote:
So i need something like - 1 CallHookPkt happens - 1 stack trace provides.