Skip to content

Commit

Permalink
Wire up some more
Browse files Browse the repository at this point in the history
  • Loading branch information
oleavr committed Sep 26, 2024
1 parent 2f2a9fe commit 910a52d
Show file tree
Hide file tree
Showing 6 changed files with 144 additions and 58 deletions.
8 changes: 8 additions & 0 deletions agents/tracer/agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,13 @@ class Agent {
}
}

resolveAddresses(addresses: string[]): string[] {
return addresses
.map(ptr)
.map(DebugSymbol.fromAddress)
.map(s => s.toString());
}

private cropStagedPlan(plan: TracePlan, id: StagedItemId): TracePlan {
let candidateId: StagedItemId;

Expand Down Expand Up @@ -1128,4 +1135,5 @@ rpc.exports = {
stageTargets: agent.stageTargets.bind(agent),
commitTargets: agent.commitTargets.bind(agent),
readMemory: agent.readMemory.bind(agent),
resolveAddresses: agent.resolveAddresses.bind(agent),
};
15 changes: 9 additions & 6 deletions apps/tracer/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ export default function App() {

events,
latestMatchingEventIndex,
highlightedEventIndex,
setHighlightedEventIndex,
selectedEventIndex,
setSelectedEventIndex,

addingTargets,
startAddingTargets,
Expand All @@ -49,6 +49,8 @@ export default function App() {
commitItems,

addInstructionHook,

symbolicate,
} = useModel();
const captureBacktracesSwitchRef = useRef<HTMLInputElement>(null);
const [selectedTabId, setSelectedTabId] = useState("events");
Expand All @@ -64,18 +66,19 @@ export default function App() {
const eventView = (
<EventView
events={events}
highlightedIndex={highlightedEventIndex}
selectedIndex={selectedEventIndex}
onActivate={(handlerId, eventIndex) => {
setSelectedHandlerId(handlerId);
setHighlightedEventIndex(eventIndex);
setSelectedEventIndex(eventIndex);
}}
onDeactivate={() => {
setHighlightedEventIndex(null);
setSelectedEventIndex(null);
}}
onDisassemble={address => {
setSelectedTabId("disassembly");
setDisassemblyTarget({ type: "instruction", address });
}}
onSymbolicate={symbolicate}
/>
);

Expand Down Expand Up @@ -121,7 +124,7 @@ export default function App() {
disabled={latestMatchingEventIndex === null}
onClick={() => {
setSelectedTabId("events");
setHighlightedEventIndex(latestMatchingEventIndex);
setSelectedEventIndex(latestMatchingEventIndex);
}}
>
Latest Event
Expand Down
6 changes: 3 additions & 3 deletions apps/tracer/src/EventView.css
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,15 @@
outline: 0;
}

.event-highlighted .event-summary {
.event-selected .event-summary {
background-color: #ef6456;
}

.event-highlighted .event-timestamp {
.event-selected .event-timestamp {
color: white !important;
}

.event-highlighted .event-message {
.event-selected .event-message {
color: white !important;
}

Expand Down
146 changes: 101 additions & 45 deletions apps/tracer/src/EventView.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,37 @@
import "./EventView.css";
import { Event, HandlerId } from "./model.js";
import { Button, Card } from "@blueprintjs/core";
import { useEffect, useRef } from "react";
import { ReactElement, useEffect, useRef, useState } from "react";
import { useStayAtBottom } from "react-stay-at-bottom";

export interface EventViewProps {
events: Event[];
highlightedIndex: number | null;
selectedIndex: number | null;
onActivate: EventActionHandler;
onDeactivate: EventActionHandler;
onDisassemble: EventDisassembleHandler;
onDisassemble: DisassembleHandler;
onSymbolicate: SymbolicateHandler;
}

export type EventActionHandler = (handlerId: HandlerId, eventIndex: number) => void;
export type EventDisassembleHandler = (address: string) => void;
export type DisassembleHandler = (address: string) => void;
export type SymbolicateHandler = (addresses: string[]) => Promise<string[]>;

const NON_BLOCKING_SPACE = "\u00A0";
const INDENT = NON_BLOCKING_SPACE.repeat(3) + "|" + NON_BLOCKING_SPACE;

export default function EventView({ events, highlightedIndex = null, onActivate, onDeactivate, onDisassemble }: EventViewProps) {
export default function EventView({
events,
selectedIndex = null,
onActivate,
onDeactivate,
onDisassemble,
onSymbolicate,
}: EventViewProps) {
const containerRef = useRef<HTMLDivElement>(null);
const highlightedRef = useRef<HTMLDivElement>(null);
const selectedRef = useRef<HTMLDivElement>(null);
const [selectedCallerSymbol, setSelectedCallerSymbol] = useState<string | null>("");
const [selectedBacktraceSymbols, setSelectedBacktraceSymbols] = useState<string[] | null>(null);
let lastTid: number | null = null;

useStayAtBottom(containerRef, {
Expand All @@ -29,7 +40,7 @@ export default function EventView({ events, highlightedIndex = null, onActivate,
});

useEffect(() => {
const item = highlightedRef.current;
const item = selectedRef.current;
if (item === null) {
return;
}
Expand All @@ -39,7 +50,84 @@ export default function EventView({ events, highlightedIndex = null, onActivate,
return;
}
item.scrollIntoView({ block: "center" });
}, [highlightedRef, highlightedIndex]);
}, [selectedRef, selectedIndex]);

useEffect(() => {
setSelectedCallerSymbol(null);
setSelectedBacktraceSymbols(null);
}, [selectedIndex]);

useEffect(() => {
if (selectedIndex === null) {
return;
}

const [_targetId, _timestamp, _threadId, _depth, caller, backtrace, _message, _style] = events[selectedIndex];
let ignore = false;

async function symbolicate() {
if (caller !== null && backtrace === null) {
const [symbol] = await onSymbolicate([caller]);
if (!ignore) {
setSelectedCallerSymbol(symbol);
}
}

if (backtrace !== null) {
const symbols = await onSymbolicate(backtrace);
if (!ignore) {
setSelectedBacktraceSymbols(symbols);
}
}
}

symbolicate();

return () => {
ignore = true;
};
}, [events, selectedIndex, onSymbolicate])

let selectedEventDetails: ReactElement | undefined;
if (selectedIndex !== null) {
const [targetId, _timestamp, threadId, _depth, caller, backtrace, _message, _style] = events[selectedIndex];

selectedEventDetails = (
<Card className="event-details" interactive={true} compact={true}>
<table>
<tbody>
<tr>
<td>Thread ID</td>
<td>0x{threadId.toString(16)}</td>
<td>
</td>
</tr>
{(caller !== null && backtrace === null) ? (
<tr>
<td>Caller</td>
<td>
<Button onClick={() => onDisassemble(caller)}>{selectedCallerSymbol ?? caller}</Button>
</td>
</tr>
) : null
}
{(backtrace !== null) ? (
<tr>
<td>Backtrace</td>
<td>
{backtrace.map((address, i) => <Button key={address} alignText="left" onClick={() => onDisassemble(address)}>
{(selectedBacktraceSymbols !== null) ? selectedBacktraceSymbols[i] : address}
</Button>)}
</td>
</tr>
) : null
}
</tbody>
</table>
<Button className="event-dismiss" intent="primary" onClick={() => onDeactivate(targetId, i)}>Dismiss</Button>
</Card>
);
}

return (
<div ref={containerRef} className="event-view">
Expand All @@ -62,16 +150,16 @@ export default function EventView({ events, highlightedIndex = null, onActivate,
lastTid = threadId;
}

const isHighlighted = i === highlightedIndex;
const isSelected = i === selectedIndex;
const eventClasses = ["event-item"];
if (isHighlighted) {
eventClasses.push("event-highlighted");
if (isSelected) {
eventClasses.push("event-selected");
}

result.push(
<div
key={i}
ref={isHighlighted ? highlightedRef : undefined}
ref={isSelected ? selectedRef : undefined}
className={eventClasses.join(" ")}
>
<div className="event-summary">
Expand All @@ -86,39 +174,7 @@ export default function EventView({ events, highlightedIndex = null, onActivate,
{message}
</Button>
</div>
{isHighlighted ? (
<Card className="event-details" interactive={true} compact={true}>
<table>
<tbody>
<tr>
<td>Thread ID</td>
<td>0x{threadId.toString(16)}</td>
<td>
</td>
</tr>
{(caller !== null && backtrace === null) ? (
<tr>
<td>Caller</td>
<td>
<Button onClick={() => onDisassemble(caller)}>{caller}</Button>
</td>
</tr>
) : null
}
{(backtrace !== null) ? (
<tr>
<td>Backtrace</td>
<td>
{backtrace.map(address => <Button key={address} onClick={() => onDisassemble(address)}>{address}</Button>)}
</td>
</tr>
) : null
}
</tbody>
</table>
<Button className="event-dismiss" intent="primary" onClick={() => onDeactivate(targetId, i)}>Dismiss</Button>
</Card>
) : null}
{isSelected ? selectedEventDetails : null}
</div>
);

Expand Down
18 changes: 15 additions & 3 deletions apps/tracer/src/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export function useModel() {
const [captureBacktraces, _setCaptureBacktraces] = useState(false);
const [events, setEvents] = useState<Event[]>([]);
const [latestMatchingEventIndex, setLatestMatchingEventIndex] = useState<number | null>(null);
const [highlightedEventIndex, setHighlightedEventIndex] = useState<number | null>(null);
const [selectedEventIndex, setSelectedEventIndex] = useState<number | null>(null);

const [addingTargets, setAddingTargets] = useState(false);
const [stagedItems, setStagedItems] = useState<StagedItem[]>([]);
Expand Down Expand Up @@ -168,6 +168,10 @@ export function useModel() {
setSelectedHandlerId(ids[0]);
}, [request]);

const symbolicate = useCallback(async (addresses: string[]): Promise<string[]> => {
return (await request("symbols:resolve-addresses", { addresses })).names;
}, [request]);

useEffect(() => {
if (lastJsonMessage === null) {
return;
Expand Down Expand Up @@ -246,8 +250,8 @@ export function useModel() {

events,
latestMatchingEventIndex,
highlightedEventIndex,
setHighlightedEventIndex,
selectedEventIndex,
setSelectedEventIndex,

addingTargets,
startAddingTargets,
Expand All @@ -257,6 +261,8 @@ export function useModel() {
commitItems,

addInstructionHook,

symbolicate,
};
}

Expand Down Expand Up @@ -358,6 +364,9 @@ interface RequestPayload {
address: string;
size: number;
};
"symbols:resolve-addresses": {
addresses: string[];
};
}
interface ResponsePayload {
"tracer:respawn": void;
Expand All @@ -375,6 +384,9 @@ interface ResponsePayload {
errors: TraceError[];
};
"memory:read": number[] | null;
"symbols:resolve-addresses": {
names: string[];
};
}

interface TraceError {
Expand Down
9 changes: 8 additions & 1 deletion frida_tools/tracer.py
Original file line number Diff line number Diff line change
Expand Up @@ -407,7 +407,7 @@ async def process_messages(self) -> None:
request_id = request.get("id")

try:
handle_request = getattr(self, "_on_" + request["type"].replace(":", "_"), None)
handle_request = getattr(self, "_on_" + request["type"].replace(":", "_").replace("-", "_"), None)
if handle_request is None:
raise NameError("unsupported request type")
result = await handle_request(request["payload"])
Expand Down Expand Up @@ -475,6 +475,10 @@ async def _on_memory_read(self, payload: dict) -> None:
data = self.app._tracer.read_memory(payload["address"], payload["size"])
return list(data) if data is not None else None

async def _on_symbols_resolve_addresses(self, payload: dict) -> None:
names = self.app._tracer.resolve_addresses(payload["addresses"])
return {"names": names}

app = TracerApplication()
app.run()

Expand Down Expand Up @@ -636,6 +640,9 @@ def commit_targets(self, identifier: Optional[int]) -> dict:
def read_memory(self, address: str, size: int) -> bytes:
return self._agent.read_memory(address, size)

def resolve_addresses(self, addresses: List[str]) -> List[str]:
return self._agent.resolve_addresses(addresses)

def _on_message(self, message, data, ui) -> None:
handled = False

Expand Down

0 comments on commit 910a52d

Please sign in to comment.