Geth clients have the
debug_traceTransaction that allows stepping through the bytecode executed during a transaction.
Using the opcodes you can determine when a contract was invoked, CALL, CALLSTATIC, DELEGATECALL, etc. From examining the opcode parameters you get the contract addresses and calldata.
With that info you can reconstruct the contract call trace for verified contracts, and from the calldata the function identifier.