Add FIFA17 Kick Off control trace profile
This commit is contained in:
@@ -2,19 +2,25 @@
|
|||||||
"""Trace FIFA17 ScenarioModeStart sources, scheduling, and publication.
|
"""Trace FIFA17 ScenarioModeStart sources, scheduling, and publication.
|
||||||
|
|
||||||
The candidate profile samples the four statically recovered publisher clusters.
|
The candidate profile samples the four statically recovered publisher clusters.
|
||||||
The scheduler profile follows the exact mode-0 chain: FreeRoam event ``0x128``,
|
The scheduler profile follows the exact mode-0 chain. The upstream profile
|
||||||
its scheduler, the ScenarioManager start method, and the mode-0 publisher. The
|
traces the three literal ``0x128`` sources. The instructions, lifecycle, and
|
||||||
upstream profile traces all three literal ``0x128`` sources and the FreeRoam
|
screen profiles follow the natural PMA state transition through event ``0x30``.
|
||||||
case they must reach.
|
The kickoff-control profile records a bounded command stream and dynamically
|
||||||
|
rotates breakpoints into the complete ScenarioModeStart chain after command
|
||||||
|
``0x128`` while retaining the consumer as an architecture-independent anchor.
|
||||||
|
Every profile respects the four-breakpoint hardware limit.
|
||||||
|
|
||||||
Both generated GDB programs use at most four simultaneously enabled hardware
|
The generated GDB programs only read registers and client memory, log, and
|
||||||
execution breakpoints. They only read registers and client memory, log, and
|
|
||||||
continue. They never call client functions, write client memory, emit events,
|
continue. They never call client functions, write client memory, emit events,
|
||||||
or drive input.
|
or drive input.
|
||||||
|
|
||||||
scenario_mode_start_trace.py [pid] --profile candidates
|
scenario_mode_start_trace.py [pid] --profile candidates
|
||||||
scenario_mode_start_trace.py [pid] --profile scheduler
|
scenario_mode_start_trace.py [pid] --profile scheduler
|
||||||
scenario_mode_start_trace.py [pid] --profile upstream
|
scenario_mode_start_trace.py [pid] --profile upstream
|
||||||
|
scenario_mode_start_trace.py [pid] --profile instructions
|
||||||
|
scenario_mode_start_trace.py [pid] --profile lifecycle
|
||||||
|
scenario_mode_start_trace.py [pid] --profile screen
|
||||||
|
scenario_mode_start_trace.py [pid] --profile kickoff-control
|
||||||
scenario_mode_start_trace.py --selftest
|
scenario_mode_start_trace.py --selftest
|
||||||
"""
|
"""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
@@ -41,6 +47,34 @@ MODE_ZERO_PUBLISHER_RVA = 0x07B1C190
|
|||||||
EVENT_SOURCE_27F9_RVA = 0x07DBD16B
|
EVENT_SOURCE_27F9_RVA = 0x07DBD16B
|
||||||
EVENT_SOURCE_30_RVA = 0x07DCA4E5
|
EVENT_SOURCE_30_RVA = 0x07DCA4E5
|
||||||
EVENT_SOURCE_280E_RVA = 0x07DD065F
|
EVENT_SOURCE_280E_RVA = 0x07DD065F
|
||||||
|
PMA_STATE_CHANGE_RVA = 0x07ADB870
|
||||||
|
PMA_COMMAND_27_CALLSITE_RVA = 0x07ADBCB2
|
||||||
|
PMA_EVENT_30_CALLSITE_RVA = 0x07ADBCF9
|
||||||
|
PMA_ADVANCE_RVA = 0x07A9FAA0
|
||||||
|
PMA_TRANSITION_DONE_CALLSITE_RVA = 0x07A9D9FD
|
||||||
|
GAMEPLAY_COMMAND_DISPATCH_RVA = 0x07A8F6C0
|
||||||
|
GAMEPLAY_GLOBAL_RVA = 0x04BFB910
|
||||||
|
PMA_INSTRUCTIONS_HANDLER_RVA = 0x07AC91E0
|
||||||
|
PMA_UI_SET_STATE_RVA = 0x07B0EBB0
|
||||||
|
TESTING_GAME_UPDATE_RVA = 0x05A410C8
|
||||||
|
SCENARIO_MODE_START_CONSUMER_RVA = 0x05A58EC0
|
||||||
|
STATE_INITIALIZER_RVA = 0x05A54CF0
|
||||||
|
STATE_INITIALIZER_DONE_RVA = 0x05A54DDB
|
||||||
|
GAME_MODE_FREE_ROAM_VTABLE_RVA = 0x03AEDF58
|
||||||
|
TESTING_GAME_VTABLE_RVA = 0x035C58A8
|
||||||
|
TESTING_GAME_STATE_VTABLE_RVA = 0x035C2EE0
|
||||||
|
|
||||||
|
OWNER_STATE_OFFSET = 0x1958
|
||||||
|
STATE_GAME_DATABASE_OFFSET = 0x17450
|
||||||
|
STATE_PHASE_OFFSET = 0x27BEC
|
||||||
|
STATE_SCENARIO_MODE_START_GATE_OFFSET = 0x359E8
|
||||||
|
DATABASE_IS_SKILL_GAME_OFFSET = 0x7382
|
||||||
|
DATABASE_TEAM_PAIR_OFFSET = 0x73C4
|
||||||
|
|
||||||
|
EVENT_SOURCE_27F9_RETURN_RVA = 0x07DBD178
|
||||||
|
EVENT_SOURCE_30_RETURN_RVA = 0x07DCA4EF
|
||||||
|
EVENT_SOURCE_280E_RETURN_RVA = 0x07DD0669
|
||||||
|
MAX_KICKOFF_DISPATCHES = 512
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -57,6 +91,25 @@ def trace_addresses(fifa_base: int) -> dict[str, int]:
|
|||||||
"event_source_27f9": fifa_base + EVENT_SOURCE_27F9_RVA,
|
"event_source_27f9": fifa_base + EVENT_SOURCE_27F9_RVA,
|
||||||
"event_source_30": fifa_base + EVENT_SOURCE_30_RVA,
|
"event_source_30": fifa_base + EVENT_SOURCE_30_RVA,
|
||||||
"event_source_280e": fifa_base + EVENT_SOURCE_280E_RVA,
|
"event_source_280e": fifa_base + EVENT_SOURCE_280E_RVA,
|
||||||
|
"pma_state_change": fifa_base + PMA_STATE_CHANGE_RVA,
|
||||||
|
"pma_command_27_callsite": fifa_base + PMA_COMMAND_27_CALLSITE_RVA,
|
||||||
|
"pma_event_30_callsite": fifa_base + PMA_EVENT_30_CALLSITE_RVA,
|
||||||
|
"pma_advance": fifa_base + PMA_ADVANCE_RVA,
|
||||||
|
"pma_transition_done_callsite": fifa_base + PMA_TRANSITION_DONE_CALLSITE_RVA,
|
||||||
|
"gameplay_command_dispatch": fifa_base + GAMEPLAY_COMMAND_DISPATCH_RVA,
|
||||||
|
"gameplay_global": fifa_base + GAMEPLAY_GLOBAL_RVA,
|
||||||
|
"pma_instructions_handler": fifa_base + PMA_INSTRUCTIONS_HANDLER_RVA,
|
||||||
|
"pma_ui_set_state": fifa_base + PMA_UI_SET_STATE_RVA,
|
||||||
|
"testing_game_update": fifa_base + TESTING_GAME_UPDATE_RVA,
|
||||||
|
"scenario_mode_start_consumer": fifa_base + SCENARIO_MODE_START_CONSUMER_RVA,
|
||||||
|
"state_initializer": fifa_base + STATE_INITIALIZER_RVA,
|
||||||
|
"state_initializer_done": fifa_base + STATE_INITIALIZER_DONE_RVA,
|
||||||
|
"game_mode_free_roam_vtable": fifa_base + GAME_MODE_FREE_ROAM_VTABLE_RVA,
|
||||||
|
"testing_game_vtable": fifa_base + TESTING_GAME_VTABLE_RVA,
|
||||||
|
"testing_game_state_vtable": fifa_base + TESTING_GAME_STATE_VTABLE_RVA,
|
||||||
|
"event_source_27f9_return": fifa_base + EVENT_SOURCE_27F9_RETURN_RVA,
|
||||||
|
"event_source_30_return": fifa_base + EVENT_SOURCE_30_RETURN_RVA,
|
||||||
|
"event_source_280e_return": fifa_base + EVENT_SOURCE_280E_RETURN_RVA,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -138,7 +191,7 @@ commands
|
|||||||
silent
|
silent
|
||||||
set $manager = $rcx
|
set $manager = $rcx
|
||||||
python import time; print("SCENARIOTRACE epoch_ns=%d mono_ns=%d SCENARIO_MANAGER_START" % (time.time_ns(), time.monotonic_ns()), end=" ")
|
python import time; print("SCENARIOTRACE epoch_ns=%d mono_ns=%d SCENARIO_MANAGER_START" % (time.time_ns(), time.monotonic_ns()), end=" ")
|
||||||
printf "thread=%d manager=%p vtable=%p requested_countdown=%d scenario_mode=%d child=%p caller_return=%p\\n", $_thread, $manager, *(void**)$manager, $edx, *(unsigned int*)($manager+0x50), *(void**)($manager+0x8), *(void**)$rsp
|
printf "thread=%d manager=%p vtable=%p skip_countdown=%d scenario_mode=%d child=%p caller_return=%p\\n", $_thread, $manager, *(void**)$manager, $dl, *(unsigned int*)($manager+0x50), *(void**)($manager+0x8), *(void**)$rsp
|
||||||
bt 16
|
bt 16
|
||||||
continue
|
continue
|
||||||
end
|
end
|
||||||
@@ -147,7 +200,7 @@ hbreak *0x{address['mode_zero_publisher']:x}
|
|||||||
commands
|
commands
|
||||||
silent
|
silent
|
||||||
python import time; print("SCENARIOTRACE epoch_ns=%d mono_ns=%d MODE_ZERO_PUBLISHER" % (time.time_ns(), time.monotonic_ns()), end=" ")
|
python import time; print("SCENARIOTRACE epoch_ns=%d mono_ns=%d MODE_ZERO_PUBLISHER" % (time.time_ns(), time.monotonic_ns()), end=" ")
|
||||||
printf "thread=%d object=%p vtable=%p requested_countdown=%d r8=%p r9=%p caller_return=%p\\n", $_thread, $rcx, *(void**)$rcx, $edx, $r8, $r9, *(void**)$rsp
|
printf "thread=%d object=%p vtable=%p context=%p scenario_value=%d skip_countdown=%d caller_return=%p\\n", $_thread, $rcx, *(void**)$rcx, $rdx, $r8d, $r9b, *(void**)$rsp
|
||||||
bt 20
|
bt 20
|
||||||
continue
|
continue
|
||||||
end
|
end
|
||||||
@@ -205,6 +258,355 @@ continue
|
|||||||
return "".join(sections)
|
return "".join(sections)
|
||||||
|
|
||||||
|
|
||||||
|
def build_instructions_script(pid: int, fifa_base: int, output: str) -> str:
|
||||||
|
address = trace_addresses(fifa_base)
|
||||||
|
return (
|
||||||
|
gdb_prelude(pid, output)
|
||||||
|
+ f"""hbreak *0x{address['pma_instructions_handler']:x}
|
||||||
|
commands
|
||||||
|
silent
|
||||||
|
set $listener = $rcx
|
||||||
|
set $parent = *(void**)($listener+0x8)
|
||||||
|
set $owner = 0
|
||||||
|
set $current = 0
|
||||||
|
if $parent != 0
|
||||||
|
set $owner = *(void**)($parent+0x8)
|
||||||
|
if $owner != 0
|
||||||
|
set $current = *(void**)($owner+0x10)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
python import time; print("SCENARIOTRACE epoch_ns=%d mono_ns=%d PMA_INSTRUCTIONS_EVENT" % (time.time_ns(), time.monotonic_ns()), end=" ")
|
||||||
|
printf "thread=%d listener=%p event=%d flag18=%d parent=%p owner=%p current=%p current_vtable=%p caller_return=%p\\n", $_thread, $listener, $edx, *(unsigned char*)($listener+0x18), $parent, $owner, $current, $current ? *(void**)$current : 0, *(void**)$rsp
|
||||||
|
bt 20
|
||||||
|
continue
|
||||||
|
end
|
||||||
|
|
||||||
|
hbreak *0x{address['pma_ui_set_state']:x}
|
||||||
|
commands
|
||||||
|
silent
|
||||||
|
set $parent = $rcx
|
||||||
|
set $owner = *(void**)($parent+0x8)
|
||||||
|
set $current = 0
|
||||||
|
if $owner != 0
|
||||||
|
set $current = *(void**)($owner+0x10)
|
||||||
|
end
|
||||||
|
python import time; print("SCENARIOTRACE epoch_ns=%d mono_ns=%d PMA_UI_SET_STATE" % (time.time_ns(), time.monotonic_ns()), end=" ")
|
||||||
|
printf "thread=%d parent=%p requested_state=%d owner=%p current=%p current_vtable=%p caller_return=%p\\n", $_thread, $parent, $edx, $owner, $current, $current ? *(void**)$current : 0, *(void**)$rsp
|
||||||
|
bt 20
|
||||||
|
continue
|
||||||
|
end
|
||||||
|
|
||||||
|
hbreak *0x{address['pma_state_change']:x}
|
||||||
|
commands
|
||||||
|
silent
|
||||||
|
set $state = $rcx
|
||||||
|
python import time; print("SCENARIOTRACE epoch_ns=%d mono_ns=%d PMA_STATE_CHANGE" % (time.time_ns(), time.monotonic_ns()), end=" ")
|
||||||
|
printf "thread=%d object=%p mode_state=%d old_state=%d new_state=%d reason=%d flag14=%d caller_return=%p\\n", $_thread, $state, *(int*)($state+0x8), *(int*)($state+0xc), $edx, $r8d, *(unsigned char*)($state+0x14), *(void**)$rsp
|
||||||
|
bt 20
|
||||||
|
continue
|
||||||
|
end
|
||||||
|
|
||||||
|
hbreak *0x{address['pma_command_27_callsite']:x}
|
||||||
|
commands
|
||||||
|
silent
|
||||||
|
python import time; print("SCENARIOTRACE epoch_ns=%d mono_ns=%d PMA_COMMAND_27" % (time.time_ns(), time.monotonic_ns()), end=" ")
|
||||||
|
printf "thread=%d state_object=%p mode_state=%d transition_state=%d new_state=%d caller_return=%p\\n", $_thread, $r13, *(int*)($r13+0x8), *(int*)($r13+0xc), $r15d, *(void**)$rsp
|
||||||
|
bt 20
|
||||||
|
continue
|
||||||
|
end
|
||||||
|
|
||||||
|
printf "SCENARIOTRACE ARMED profile=instructions pid={pid} instructions=0x{address['pma_instructions_handler']:x} ui_set_state=0x{address['pma_ui_set_state']:x} state_change=0x{address['pma_state_change']:x} command27=0x{address['pma_command_27_callsite']:x}\\n"
|
||||||
|
continue
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def build_lifecycle_script(pid: int, fifa_base: int, output: str) -> str:
|
||||||
|
address = trace_addresses(fifa_base)
|
||||||
|
return (
|
||||||
|
gdb_prelude(pid, output)
|
||||||
|
+ f"""hbreak *0x{address['pma_state_change']:x}
|
||||||
|
commands
|
||||||
|
silent
|
||||||
|
set $state = $rcx
|
||||||
|
set $gameplay_global = *(void**)0x{address['gameplay_global']:x}
|
||||||
|
set $listener_manager = 0
|
||||||
|
set $listener_table = 0
|
||||||
|
set $listener_index = -1
|
||||||
|
set $selected_listener = 0
|
||||||
|
if $gameplay_global != 0
|
||||||
|
set $listener_manager = *(void**)($gameplay_global+0x58)
|
||||||
|
if $listener_manager != 0
|
||||||
|
set $listener_table = *(void**)$listener_manager
|
||||||
|
if $listener_table != 0
|
||||||
|
set $listener_index = *(int*)($listener_table+0x20)
|
||||||
|
if $listener_index >= 0
|
||||||
|
set $selected_listener = *(void**)($listener_table+$listener_index*8)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
python import time; print("SCENARIOTRACE epoch_ns=%d mono_ns=%d PMA_STATE_CHANGE" % (time.time_ns(), time.monotonic_ns()), end=" ")
|
||||||
|
printf "thread=%d object=%p mode_state=%d old_state=%d new_state=%d reason=%d flag14=%d selected_index=%d selected=%p selected_vtable=%p caller_return=%p\\n", $_thread, $state, *(int*)($state+0x8), *(int*)($state+0xc), $edx, $r8d, *(unsigned char*)($state+0x14), $listener_index, $selected_listener, $selected_listener ? *(void**)$selected_listener : 0, *(void**)$rsp
|
||||||
|
bt 20
|
||||||
|
continue
|
||||||
|
end
|
||||||
|
|
||||||
|
hbreak *0x{address['pma_command_27_callsite']:x}
|
||||||
|
commands
|
||||||
|
silent
|
||||||
|
python import time; print("SCENARIOTRACE epoch_ns=%d mono_ns=%d PMA_COMMAND_27" % (time.time_ns(), time.monotonic_ns()), end=" ")
|
||||||
|
printf "thread=%d state_object=%p mode_state=%d transition_state=%d new_state=%d selected_state=%d caller_return=%p\\n", $_thread, $r13, *(int*)($r13+0x8), *(int*)($r13+0xc), $r15d, *(int*)($r13+0xc), *(void**)$rsp
|
||||||
|
bt 20
|
||||||
|
continue
|
||||||
|
end
|
||||||
|
|
||||||
|
hbreak *0x{address['pma_advance']:x}
|
||||||
|
commands
|
||||||
|
silent
|
||||||
|
python import time; print("SCENARIOTRACE epoch_ns=%d mono_ns=%d PMA_ADVANCE_FROM_PMA" % (time.time_ns(), time.monotonic_ns()), end=" ")
|
||||||
|
printf "thread=%d free_roam=%p old_substate=%d guard124=%d manager=%p caller_return=%p\\n", $_thread, $rcx, *(int*)($rcx+0x30), *(int*)($rcx+0x124), *(void**)($rcx+0x168), *(void**)$rsp
|
||||||
|
bt 20
|
||||||
|
continue
|
||||||
|
end
|
||||||
|
|
||||||
|
hbreak *0x{address['pma_transition_done_callsite']:x}
|
||||||
|
commands
|
||||||
|
silent
|
||||||
|
python import time; print("SCENARIOTRACE epoch_ns=%d mono_ns=%d PMA_TRANSITION_DONE" % (time.time_ns(), time.monotonic_ns()), end=" ")
|
||||||
|
printf "thread=%d free_roam=%p substate=%d event=%#x event_target=%p target_vtable=%p caller_return=%p\\n", $_thread, $r14, *(int*)($r14+0x30), $edx, $rbx, *(void**)$rbx, *(void**)$rsp
|
||||||
|
bt 20
|
||||||
|
continue
|
||||||
|
end
|
||||||
|
|
||||||
|
printf "SCENARIOTRACE ARMED profile=lifecycle pid={pid} state_change=0x{address['pma_state_change']:x} command27=0x{address['pma_command_27_callsite']:x} advance=0x{address['pma_advance']:x} transition_done=0x{address['pma_transition_done_callsite']:x}\\n"
|
||||||
|
continue
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def build_screen_script(pid: int, fifa_base: int, output: str) -> str:
|
||||||
|
address = trace_addresses(fifa_base)
|
||||||
|
return (
|
||||||
|
gdb_prelude(pid, output)
|
||||||
|
+ f"""hbreak *0x{address['pma_state_change']:x}
|
||||||
|
commands
|
||||||
|
silent
|
||||||
|
set $state = $rcx
|
||||||
|
python import time; print("SCENARIOTRACE epoch_ns=%d mono_ns=%d PMA_STATE_CHANGE" % (time.time_ns(), time.monotonic_ns()), end=" ")
|
||||||
|
printf "thread=%d object=%p mode_state=%d old_state=%d new_state=%d reason=%d flag14=%d caller_return=%p\\n", $_thread, $state, *(int*)($state+0x8), *(int*)($state+0xc), $edx, $r8d, *(unsigned char*)($state+0x14), *(void**)$rsp
|
||||||
|
bt 20
|
||||||
|
continue
|
||||||
|
end
|
||||||
|
|
||||||
|
hbreak *0x{address['pma_event_30_callsite']:x}
|
||||||
|
commands
|
||||||
|
silent
|
||||||
|
python import time; print("SCENARIOTRACE epoch_ns=%d mono_ns=%d PMA_EVENT_30" % (time.time_ns(), time.monotonic_ns()), end=" ")
|
||||||
|
printf "thread=%d state_object=%p mode_state=%d transition_state=%d selected=%p selected_vtable=%p caller_return=%p\\n", $_thread, $r13, *(int*)($r13+0x8), *(int*)($r13+0xc), $rcx, *(void**)$rcx, *(void**)$rsp
|
||||||
|
bt 20
|
||||||
|
continue
|
||||||
|
end
|
||||||
|
|
||||||
|
hbreak *0x{address['gameplay_command_dispatch']:x}
|
||||||
|
condition 3 $edx == 0x128
|
||||||
|
commands
|
||||||
|
silent
|
||||||
|
python import time; print("SCENARIOTRACE epoch_ns=%d mono_ns=%d GAMEPLAY_COMMAND_128" % (time.time_ns(), time.monotonic_ns()), end=" ")
|
||||||
|
printf "thread=%d dispatcher=%p command=%#x listeners=%p caller_return=%p\\n", $_thread, $rcx, $edx, *(void**)$rcx, *(void**)$rsp
|
||||||
|
bt 20
|
||||||
|
continue
|
||||||
|
end
|
||||||
|
|
||||||
|
hbreak *0x{address['mode_zero_publisher']:x}
|
||||||
|
commands
|
||||||
|
silent
|
||||||
|
python import time; print("SCENARIOTRACE epoch_ns=%d mono_ns=%d MODE_ZERO_PUBLISHER" % (time.time_ns(), time.monotonic_ns()), end=" ")
|
||||||
|
printf "thread=%d object=%p vtable=%p requested_countdown=%d caller_return=%p\\n", $_thread, $rcx, *(void**)$rcx, $edx, *(void**)$rsp
|
||||||
|
bt 20
|
||||||
|
continue
|
||||||
|
end
|
||||||
|
|
||||||
|
printf "SCENARIOTRACE ARMED profile=screen pid={pid} state_change=0x{address['pma_state_change']:x} event30=0x{address['pma_event_30_callsite']:x} command128=0x{address['gameplay_command_dispatch']:x} publisher=0x{address['mode_zero_publisher']:x}\\n"
|
||||||
|
continue
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def build_kickoff_control_script(pid: int, fifa_base: int, output: str) -> str:
|
||||||
|
address = trace_addresses(fifa_base)
|
||||||
|
return (
|
||||||
|
gdb_prelude(pid, output)
|
||||||
|
+ f"""set breakpoint always-inserted off
|
||||||
|
set $dispatch_count = 0
|
||||||
|
set $command_128_seen = 0
|
||||||
|
|
||||||
|
hbreak *0x{address['gameplay_command_dispatch']:x}
|
||||||
|
commands
|
||||||
|
silent
|
||||||
|
set $dispatch_count = $dispatch_count + 1
|
||||||
|
set $caller = *(void**)$rsp
|
||||||
|
set $listeners = *(void**)$rcx
|
||||||
|
set $listener0 = 0
|
||||||
|
set $listener1 = 0
|
||||||
|
set $listener2 = 0
|
||||||
|
set $vtable0 = 0
|
||||||
|
set $vtable1 = 0
|
||||||
|
set $vtable2 = 0
|
||||||
|
if $listeners != 0
|
||||||
|
set $listener0 = *(void**)$listeners
|
||||||
|
set $listener1 = *(void**)($listeners+8)
|
||||||
|
set $listener2 = *(void**)($listeners+16)
|
||||||
|
end
|
||||||
|
if $listener0 != 0
|
||||||
|
set $vtable0 = *(void**)$listener0
|
||||||
|
end
|
||||||
|
if $listener1 != 0
|
||||||
|
set $vtable1 = *(void**)$listener1
|
||||||
|
end
|
||||||
|
if $listener2 != 0
|
||||||
|
set $vtable2 = *(void**)$listener2
|
||||||
|
end
|
||||||
|
python import time; print("KICKOFFTRACE epoch_ns=%d mono_ns=%d GAMEPLAY_COMMAND_DISPATCH" % (time.time_ns(), time.monotonic_ns()), end=" ")
|
||||||
|
printf "thread=%d ordinal=%d command=%#x dispatcher=%p payload=%p r9=%p caller_return=%p listeners=%p entries=%p,%p,%p vtables=%p,%p,%p free_roam_present=%d\\n", $_thread, $dispatch_count, $edx, $rcx, $r8, $r9, $caller, $listeners, $listener0, $listener1, $listener2, $vtable0, $vtable1, $vtable2, $vtable0==0x{address['game_mode_free_roam_vtable']:x} || $vtable1==0x{address['game_mode_free_roam_vtable']:x} || $vtable2==0x{address['game_mode_free_roam_vtable']:x}
|
||||||
|
if $caller == 0x{address['event_source_27f9_return']:x}
|
||||||
|
python import time; print("KICKOFFTRACE epoch_ns=%d mono_ns=%d SOURCE_EVENT_27F9" % (time.time_ns(), time.monotonic_ns()), end=" ")
|
||||||
|
printf "thread=%d event=%#x object=%p vtable=%p payload=%p handler=0x{fifa_base + 0x07DBD070:x} caller_return=%p\\n", $_thread, $ebp, $rsi, *(void**)$rsi, $rdi, $caller
|
||||||
|
end
|
||||||
|
if $caller == 0x{address['event_source_30_return']:x}
|
||||||
|
python import time; print("KICKOFFTRACE epoch_ns=%d mono_ns=%d SOURCE_EVENT_0030" % (time.time_ns(), time.monotonic_ns()), end=" ")
|
||||||
|
printf "thread=%d event=%#x object=%p vtable=%p payload=%p handler=0x{fifa_base + 0x07DCA400:x} caller_return=%p\\n", $_thread, $ebx, $rsi, *(void**)$rsi, $rdi, $caller
|
||||||
|
end
|
||||||
|
if $caller == 0x{address['event_source_280e_return']:x}
|
||||||
|
python import time; print("KICKOFFTRACE epoch_ns=%d mono_ns=%d SOURCE_EVENT_280E" % (time.time_ns(), time.monotonic_ns()), end=" ")
|
||||||
|
printf "thread=%d event=0x280e object=%p vtable=%p payload=%p handler=0x{fifa_base + 0x07DD0280:x} caller_return=%p\\n", $_thread, $rbx, *(void**)$rbx, $r10, $caller
|
||||||
|
end
|
||||||
|
if $edx == 0x128
|
||||||
|
set $command_128_seen = 1
|
||||||
|
disable 4
|
||||||
|
enable 7
|
||||||
|
bt 20
|
||||||
|
end
|
||||||
|
if $dispatch_count >= {MAX_KICKOFF_DISPATCHES}
|
||||||
|
printf "KICKOFFTRACE DISPATCH_LIMIT_REACHED count=%d\\n", $dispatch_count
|
||||||
|
disable 1
|
||||||
|
end
|
||||||
|
continue
|
||||||
|
end
|
||||||
|
|
||||||
|
hbreak *0x{address['mode_zero_publisher']:x}
|
||||||
|
commands
|
||||||
|
silent
|
||||||
|
python import time; print("KICKOFFTRACE epoch_ns=%d mono_ns=%d MODE_ZERO_PUBLISHER" % (time.time_ns(), time.monotonic_ns()), end=" ")
|
||||||
|
printf "thread=%d object=%p vtable=%p context=%p scenario_value=%d skip_countdown=%d caller_return=%p\\n", $_thread, $rcx, *(void**)$rcx, $rdx, $r8d, $r9b, *(void**)$rsp
|
||||||
|
bt 20
|
||||||
|
disable 2
|
||||||
|
continue
|
||||||
|
end
|
||||||
|
|
||||||
|
hbreak *0x{address['scenario_mode_start_consumer']:x}
|
||||||
|
commands
|
||||||
|
silent
|
||||||
|
set $wrapper = $rcx
|
||||||
|
set $state = *(void**)($wrapper+0x30)
|
||||||
|
set $database = 0
|
||||||
|
if $state != 0
|
||||||
|
set $database = *(void**)($state+0x{STATE_GAME_DATABASE_OFFSET:x})
|
||||||
|
end
|
||||||
|
python import time; print("KICKOFFTRACE epoch_ns=%d mono_ns=%d SCENARIO_MODE_START_CONSUMER" % (time.time_ns(), time.monotonic_ns()), end=" ")
|
||||||
|
if $database != 0
|
||||||
|
printf "thread=%d wrapper=%p wrapper_vtable=%p state=%p state_vtable=%p payload_provider=%p phase_before=%d alternate_gate_before=%d database=%p is_skill_game=%d teams=%d,%d caller_return=%p\\n", $_thread, $wrapper, *(void**)$wrapper, $state, *(void**)$state, $r9, *(unsigned int*)($state+0x{STATE_PHASE_OFFSET:x}), *(unsigned char*)($state+0x{STATE_SCENARIO_MODE_START_GATE_OFFSET:x}), $database, *(unsigned char*)($database+0x{DATABASE_IS_SKILL_GAME_OFFSET:x}), *(unsigned int*)($database+0x{DATABASE_TEAM_PAIR_OFFSET:x}), *(unsigned int*)($database+0x{DATABASE_TEAM_PAIR_OFFSET + 4:x}), *(void**)$rsp
|
||||||
|
else
|
||||||
|
printf "thread=%d wrapper=%p state=%p payload_provider=%p database=0 caller_return=%p\\n", $_thread, $wrapper, $state, $r9, *(void**)$rsp
|
||||||
|
end
|
||||||
|
bt 20
|
||||||
|
disable 1
|
||||||
|
disable 2
|
||||||
|
disable 7
|
||||||
|
enable 5
|
||||||
|
continue
|
||||||
|
end
|
||||||
|
|
||||||
|
hbreak *0x{address['testing_game_update']:x}
|
||||||
|
commands
|
||||||
|
silent
|
||||||
|
set $owner = $rsi
|
||||||
|
set $state = *(void**)($owner+0x{OWNER_STATE_OFFSET:x})
|
||||||
|
python import time; print("KICKOFFTRACE epoch_ns=%d mono_ns=%d GAMEPLAY_UPDATE_POSITIVE" % (time.time_ns(), time.monotonic_ns()), end=" ")
|
||||||
|
printf "thread=%d owner=%p owner_vtable=%p state=%p state_vtable=%p expected_owner=%d expected_state=%d\\n", $_thread, $owner, *(void**)$owner, $state, *(void**)$state, *(void**)$owner==0x{address['testing_game_vtable']:x}, *(void**)$state==0x{address['testing_game_state_vtable']:x}
|
||||||
|
disable 4
|
||||||
|
continue
|
||||||
|
end
|
||||||
|
|
||||||
|
hbreak *0x{address['state_initializer']:x}
|
||||||
|
commands
|
||||||
|
silent
|
||||||
|
set $state = $rcx
|
||||||
|
set $database = *(void**)($state+0x{STATE_GAME_DATABASE_OFFSET:x})
|
||||||
|
python import time; print("KICKOFFTRACE epoch_ns=%d mono_ns=%d STATE_INITIALIZER" % (time.time_ns(), time.monotonic_ns()), end=" ")
|
||||||
|
printf "thread=%d state=%p state_vtable=%p payload_provider=%p phase_before=%d alternate_gate_before=%d database=%p is_skill_game=%d teams=%d,%d caller_return=%p\\n", $_thread, $state, *(void**)$state, $rdx, *(unsigned int*)($state+0x{STATE_PHASE_OFFSET:x}), *(unsigned char*)($state+0x{STATE_SCENARIO_MODE_START_GATE_OFFSET:x}), $database, *(unsigned char*)($database+0x{DATABASE_IS_SKILL_GAME_OFFSET:x}), *(unsigned int*)($database+0x{DATABASE_TEAM_PAIR_OFFSET:x}), *(unsigned int*)($database+0x{DATABASE_TEAM_PAIR_OFFSET + 4:x}), *(void**)$rsp
|
||||||
|
disable 3
|
||||||
|
disable 5
|
||||||
|
enable 6
|
||||||
|
continue
|
||||||
|
end
|
||||||
|
disable 5
|
||||||
|
|
||||||
|
hbreak *0x{address['state_initializer_done']:x}
|
||||||
|
commands
|
||||||
|
silent
|
||||||
|
set $state = $rbx
|
||||||
|
set $database = *(void**)($state+0x{STATE_GAME_DATABASE_OFFSET:x})
|
||||||
|
python import time; print("KICKOFFTRACE epoch_ns=%d mono_ns=%d STATE_INITIALIZER_DONE" % (time.time_ns(), time.monotonic_ns()), end=" ")
|
||||||
|
printf "thread=%d state=%p payload_provider=%p phase_after=%d alternate_gate_after=%d database=%p is_skill_game=%d teams=%d,%d\\n", $_thread, $state, $rdi, *(unsigned int*)($state+0x{STATE_PHASE_OFFSET:x}), *(unsigned char*)($state+0x{STATE_SCENARIO_MODE_START_GATE_OFFSET:x}), $database, *(unsigned char*)($database+0x{DATABASE_IS_SKILL_GAME_OFFSET:x}), *(unsigned int*)($database+0x{DATABASE_TEAM_PAIR_OFFSET:x}), *(unsigned int*)($database+0x{DATABASE_TEAM_PAIR_OFFSET + 4:x})
|
||||||
|
disable 6
|
||||||
|
continue
|
||||||
|
end
|
||||||
|
disable 6
|
||||||
|
|
||||||
|
hbreak *0x{address['free_roam_event_128_callsite']:x}
|
||||||
|
commands
|
||||||
|
silent
|
||||||
|
set $owner = $rbx
|
||||||
|
python import time; print("KICKOFFTRACE epoch_ns=%d mono_ns=%d FREE_ROAM_EVENT_128" % (time.time_ns(), time.monotonic_ns()), end=" ")
|
||||||
|
printf "thread=%d owner=%p owner_vtable=%p command=%#x payload=%p ready=%d manager=%p caller_return=%p\\n", $_thread, $owner, *(void**)$owner, $esi, $rdi, *(unsigned char*)($owner+0x124), *(void**)($owner+0x168), *(void**)$rsp
|
||||||
|
disable 1
|
||||||
|
disable 7
|
||||||
|
enable 8
|
||||||
|
continue
|
||||||
|
end
|
||||||
|
disable 7
|
||||||
|
|
||||||
|
hbreak *0x{address['scenario_scheduler']:x}
|
||||||
|
commands
|
||||||
|
silent
|
||||||
|
set $owner = $rcx
|
||||||
|
python import time; print("KICKOFFTRACE epoch_ns=%d mono_ns=%d SCENARIO_SCHEDULER" % (time.time_ns(), time.monotonic_ns()), end=" ")
|
||||||
|
printf "thread=%d owner=%p owner_vtable=%p command=%#x payload=%p ready=%d manager=%p caller_return=%p\\n", $_thread, $owner, *(void**)$owner, $edx, $r8, *(unsigned char*)($owner+0x124), *(void**)($owner+0x168), *(void**)$rsp
|
||||||
|
disable 8
|
||||||
|
enable 9
|
||||||
|
continue
|
||||||
|
end
|
||||||
|
disable 8
|
||||||
|
|
||||||
|
hbreak *0x{address['scenario_manager_start']:x}
|
||||||
|
commands
|
||||||
|
silent
|
||||||
|
set $manager = $rcx
|
||||||
|
set $child = *(void**)($manager+0x8)
|
||||||
|
python import time; print("KICKOFFTRACE epoch_ns=%d mono_ns=%d SCENARIO_MANAGER_START" % (time.time_ns(), time.monotonic_ns()), end=" ")
|
||||||
|
printf "thread=%d manager=%p manager_vtable=%p skip_countdown=%d mode=%d child=%p child_vtable=%p caller_return=%p\\n", $_thread, $manager, *(void**)$manager, $dl, *(unsigned int*)($manager+0x50), $child, *(void**)$child, *(void**)$rsp
|
||||||
|
disable 9
|
||||||
|
continue
|
||||||
|
end
|
||||||
|
disable 9
|
||||||
|
|
||||||
|
printf "KICKOFFTRACE ARMED profile=kickoff-control pid={pid} dispatch=0x{address['gameplay_command_dispatch']:x} publisher=0x{address['mode_zero_publisher']:x} consumer=0x{address['scenario_mode_start_consumer']:x} update=0x{address['testing_game_update']:x} initializer=0x{address['state_initializer']:x} free_roam=0x{address['free_roam_event_128_callsite']:x} scheduler=0x{address['scenario_scheduler']:x} manager=0x{address['scenario_manager_start']:x} max_dispatches={MAX_KICKOFF_DISPATCHES}\\n"
|
||||||
|
continue
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def selftest() -> None:
|
def selftest() -> None:
|
||||||
address = trace_addresses(0x140000000)
|
address = trace_addresses(0x140000000)
|
||||||
assert address["candidate_a"] == 0x147AFDCC0
|
assert address["candidate_a"] == 0x147AFDCC0
|
||||||
@@ -218,9 +620,33 @@ def selftest() -> None:
|
|||||||
assert address["event_source_27f9"] == 0x147DBD16B
|
assert address["event_source_27f9"] == 0x147DBD16B
|
||||||
assert address["event_source_30"] == 0x147DCA4E5
|
assert address["event_source_30"] == 0x147DCA4E5
|
||||||
assert address["event_source_280e"] == 0x147DD065F
|
assert address["event_source_280e"] == 0x147DD065F
|
||||||
|
assert address["pma_state_change"] == 0x147ADB870
|
||||||
|
assert address["pma_command_27_callsite"] == 0x147ADBCB2
|
||||||
|
assert address["pma_event_30_callsite"] == 0x147ADBCF9
|
||||||
|
assert address["pma_advance"] == 0x147A9FAA0
|
||||||
|
assert address["pma_transition_done_callsite"] == 0x147A9D9FD
|
||||||
|
assert address["gameplay_command_dispatch"] == 0x147A8F6C0
|
||||||
|
assert address["pma_instructions_handler"] == 0x147AC91E0
|
||||||
|
assert address["pma_ui_set_state"] == 0x147B0EBB0
|
||||||
|
assert address["testing_game_update"] == 0x145A410C8
|
||||||
|
assert address["scenario_mode_start_consumer"] == 0x145A58EC0
|
||||||
|
assert address["state_initializer"] == 0x145A54CF0
|
||||||
|
assert address["state_initializer_done"] == 0x145A54DDB
|
||||||
|
assert address["game_mode_free_roam_vtable"] == 0x143AEDF58
|
||||||
|
assert address["event_source_27f9_return"] == 0x147DBD178
|
||||||
|
assert address["event_source_30_return"] == 0x147DCA4EF
|
||||||
|
assert address["event_source_280e_return"] == 0x147DD0669
|
||||||
candidate_script = build_candidate_script(61470, 0x140000000, "/tmp/candidates.log")
|
candidate_script = build_candidate_script(61470, 0x140000000, "/tmp/candidates.log")
|
||||||
scheduler_script = build_scheduler_script(61470, 0x140000000, "/tmp/scheduler.log")
|
scheduler_script = build_scheduler_script(61470, 0x140000000, "/tmp/scheduler.log")
|
||||||
upstream_script = build_upstream_script(61470, 0x140000000, "/tmp/upstream.log")
|
upstream_script = build_upstream_script(61470, 0x140000000, "/tmp/upstream.log")
|
||||||
|
lifecycle_script = build_lifecycle_script(61470, 0x140000000, "/tmp/lifecycle.log")
|
||||||
|
screen_script = build_screen_script(61470, 0x140000000, "/tmp/screen.log")
|
||||||
|
instructions_script = build_instructions_script(
|
||||||
|
61470, 0x140000000, "/tmp/instructions.log"
|
||||||
|
)
|
||||||
|
kickoff_script = build_kickoff_control_script(
|
||||||
|
61470, 0x140000000, "/tmp/kickoff-control.log"
|
||||||
|
)
|
||||||
assert candidate_script.count("hbreak *") == 4
|
assert candidate_script.count("hbreak *") == 4
|
||||||
assert all(f"CANDIDATE_{label}" in candidate_script for label in "ABCD")
|
assert all(f"CANDIDATE_{label}" in candidate_script for label in "ABCD")
|
||||||
assert scheduler_script.count("hbreak *") == 4
|
assert scheduler_script.count("hbreak *") == 4
|
||||||
@@ -233,11 +659,41 @@ def selftest() -> None:
|
|||||||
assert "EVENT_128_SOURCE_30" in upstream_script
|
assert "EVENT_128_SOURCE_30" in upstream_script
|
||||||
assert "EVENT_128_SOURCE_280E" in upstream_script
|
assert "EVENT_128_SOURCE_280E" in upstream_script
|
||||||
assert "FREE_ROAM_EVENT_128" in upstream_script
|
assert "FREE_ROAM_EVENT_128" in upstream_script
|
||||||
|
assert instructions_script.count("hbreak *") == 4
|
||||||
|
assert "PMA_INSTRUCTIONS_EVENT" in instructions_script
|
||||||
|
assert "PMA_UI_SET_STATE" in instructions_script
|
||||||
|
assert "PMA_STATE_CHANGE" in instructions_script
|
||||||
|
assert "PMA_COMMAND_27" in instructions_script
|
||||||
|
assert lifecycle_script.count("hbreak *") == 4
|
||||||
|
assert "PMA_STATE_CHANGE" in lifecycle_script
|
||||||
|
assert "PMA_COMMAND_27" in lifecycle_script
|
||||||
|
assert "PMA_ADVANCE_FROM_PMA" in lifecycle_script
|
||||||
|
assert "PMA_TRANSITION_DONE" in lifecycle_script
|
||||||
|
assert screen_script.count("hbreak *") == 4
|
||||||
|
assert "PMA_EVENT_30" in screen_script
|
||||||
|
assert "GAMEPLAY_COMMAND_128" in screen_script
|
||||||
|
assert "MODE_ZERO_PUBLISHER" in screen_script
|
||||||
|
assert kickoff_script.count("hbreak *") == 9
|
||||||
|
assert "GAMEPLAY_COMMAND_DISPATCH" in kickoff_script
|
||||||
|
assert "GAMEPLAY_UPDATE_POSITIVE" in kickoff_script
|
||||||
|
assert "SOURCE_EVENT_27F9" in kickoff_script
|
||||||
|
assert "SOURCE_EVENT_0030" in kickoff_script
|
||||||
|
assert "SOURCE_EVENT_280E" in kickoff_script
|
||||||
|
assert "FREE_ROAM_EVENT_128" in kickoff_script
|
||||||
|
assert "SCENARIO_SCHEDULER" in kickoff_script
|
||||||
|
assert "SCENARIO_MANAGER_START" in kickoff_script
|
||||||
|
assert "MODE_ZERO_PUBLISHER" in kickoff_script
|
||||||
|
assert "SCENARIO_MODE_START_CONSUMER" in kickoff_script
|
||||||
|
assert "STATE_INITIALIZER_DONE" in kickoff_script
|
||||||
|
assert "set *(" not in kickoff_script
|
||||||
assert "0x147ac9900" not in scheduler_script.lower()
|
assert "0x147ac9900" not in scheduler_script.lower()
|
||||||
assert "CANDIDATE_B_GATE" not in scheduler_script
|
assert "CANDIDATE_B_GATE" not in scheduler_script
|
||||||
assert "set *(" not in candidate_script
|
assert "set *(" not in candidate_script
|
||||||
assert "set *(" not in scheduler_script
|
assert "set *(" not in scheduler_script
|
||||||
assert "set *(" not in upstream_script
|
assert "set *(" not in upstream_script
|
||||||
|
assert "set *(" not in lifecycle_script
|
||||||
|
assert "set *(" not in screen_script
|
||||||
|
assert "set *(" not in instructions_script
|
||||||
print("scenario_mode_start_trace selftest: PASS")
|
print("scenario_mode_start_trace selftest: PASS")
|
||||||
|
|
||||||
|
|
||||||
@@ -246,7 +702,15 @@ def main() -> int:
|
|||||||
parser.add_argument("pid", nargs="?", type=int)
|
parser.add_argument("pid", nargs="?", type=int)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--profile",
|
"--profile",
|
||||||
choices=("candidates", "scheduler", "upstream"),
|
choices=(
|
||||||
|
"candidates",
|
||||||
|
"scheduler",
|
||||||
|
"upstream",
|
||||||
|
"instructions",
|
||||||
|
"lifecycle",
|
||||||
|
"screen",
|
||||||
|
"kickoff-control",
|
||||||
|
),
|
||||||
default="candidates",
|
default="candidates",
|
||||||
)
|
)
|
||||||
parser.add_argument("--output")
|
parser.add_argument("--output")
|
||||||
@@ -273,8 +737,16 @@ def main() -> int:
|
|||||||
script = build_candidate_script(pid, fifa_base, output)
|
script = build_candidate_script(pid, fifa_base, output)
|
||||||
elif args.profile == "scheduler":
|
elif args.profile == "scheduler":
|
||||||
script = build_scheduler_script(pid, fifa_base, output)
|
script = build_scheduler_script(pid, fifa_base, output)
|
||||||
else:
|
elif args.profile == "upstream":
|
||||||
script = build_upstream_script(pid, fifa_base, output)
|
script = build_upstream_script(pid, fifa_base, output)
|
||||||
|
elif args.profile == "instructions":
|
||||||
|
script = build_instructions_script(pid, fifa_base, output)
|
||||||
|
elif args.profile == "lifecycle":
|
||||||
|
script = build_lifecycle_script(pid, fifa_base, output)
|
||||||
|
elif args.profile == "screen":
|
||||||
|
script = build_screen_script(pid, fifa_base, output)
|
||||||
|
else:
|
||||||
|
script = build_kickoff_control_script(pid, fifa_base, output)
|
||||||
except (OSError, RuntimeError, ValueError) as error:
|
except (OSError, RuntimeError, ValueError) as error:
|
||||||
print(error, file=sys.stderr)
|
print(error, file=sys.stderr)
|
||||||
return 2
|
return 2
|
||||||
|
|||||||
Reference in New Issue
Block a user