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 23, 2024
1 parent 03f63c4 commit 71b3a3f
Show file tree
Hide file tree
Showing 6 changed files with 118 additions and 40 deletions.
59 changes: 43 additions & 16 deletions agents/tracer/agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class Agent {
try {
(1, eval)(script.source);
} catch (e: any) {
throw new Error(`Unable to load ${script.filename}: ${e.stack}`);
throw new Error(`unable to load ${script.filename}: ${e.stack}`);
}
}

Expand All @@ -51,17 +51,26 @@ class Agent {
this.flush();
}

update(id: TraceTargetId, name: string, script: HandlerScript) {
updateHandlerCode(id: TraceTargetId, name: string, script: HandlerScript) {
const handler = this.handlers.get(id);
if (handler === undefined) {
throw new Error("Invalid target ID");
throw new Error("invalid target ID");
}

const newHandler = this.parseHandler(name, script);
handler[0] = newHandler[0];
handler[1] = newHandler[1];
}

updateHandlerConfig(id: TraceTargetId, config: HandlerConfig) {
const handler = this.handlers.get(id);
if (handler === undefined) {
throw new Error("invalid target ID");
}

handler[2] = config;
}

async stageTargets(spec: TraceSpec): Promise<StagedItem[]> {
const request = await this.createPlan(spec);
this.stagedPlanRequest = request;
Expand Down Expand Up @@ -421,10 +430,12 @@ class Agent {

return {
onEnter(args) {
agent.invokeNativeHandler(id, handler[0], this, args, ">");
const [onEnter, _, config] = handler;
agent.invokeNativeHandler(id, onEnter, config, this, args, ">");
},
onLeave(retval) {
agent.invokeNativeHandler(id, handler[1], this, retval, "<");
const [_, onLeave, config] = handler;
agent.invokeNativeHandler(id, onLeave, config, this, retval, "<");
}
};
}
Expand All @@ -438,35 +449,40 @@ class Agent {
}

private handleJavaInvocation(id: TraceTargetId, method: Java.Method, handler: TraceHandler, instance: Java.Wrapper, args: any[]): any {
this.invokeJavaHandler(id, handler[0], instance, args, ">");
const [onEnter, onLeave, config] = handler;

this.invokeJavaHandler(id, onEnter, config, instance, args, ">");

const retval = method.apply(instance, args);

const replacementRetval = this.invokeJavaHandler(id, handler[1], instance, retval, "<");
const replacementRetval = this.invokeJavaHandler(id, onLeave, config, instance, retval, "<");

return (replacementRetval !== undefined) ? replacementRetval : retval;
}

private invokeNativeHandler(id: TraceTargetId, callback: TraceEnterHandler | TraceLeaveHandler, context: InvocationContext, param: any, cutPoint: CutPoint) {
private invokeNativeHandler(id: TraceTargetId, callback: TraceEnterHandler | TraceLeaveHandler, config: HandlerConfig,
context: InvocationContext, param: any, cutPoint: CutPoint) {
const timestamp = Date.now() - this.started;
const threadId = context.threadId;
const depth = this.updateDepth(threadId, cutPoint);
const caller = context.returnAddress.toString();
const backtrace = config.capture_backtraces ? Thread.backtrace(context.context).map(p => p.toString()) : null;

const log = (...message: string[]) => {
this.emit([id, timestamp, threadId, depth, caller, message.join(" ")]);
this.emit([id, timestamp, threadId, depth, caller, backtrace, message.join(" ")]);
};

callback.call(context, log, param, this.traceState);
}

private invokeJavaHandler(id: TraceTargetId, callback: TraceEnterHandler | TraceLeaveHandler, instance: Java.Wrapper, param: any, cutPoint: CutPoint) {
private invokeJavaHandler(id: TraceTargetId, callback: TraceEnterHandler | TraceLeaveHandler, config: HandlerConfig,
instance: Java.Wrapper, param: any, cutPoint: CutPoint) {
const timestamp = Date.now() - this.started;
const threadId = Process.getCurrentThreadId();
const depth = this.updateDepth(threadId, cutPoint);

const log = (...message: string[]) => {
this.emit([id, timestamp, threadId, depth, null, message.join(" ")]);
this.emit([id, timestamp, threadId, depth, null, null, message.join(" ")]);
};

try {
Expand Down Expand Up @@ -503,13 +519,13 @@ class Agent {
const id = `/handlers/${name}.js`;
try {
const h = Script.evaluate(id, script);
return [h.onEnter ?? noop, h.onLeave ?? noop];
return [h.onEnter ?? noop, h.onLeave ?? noop, makeDefaultHandlerConfig()];
} catch (e: any) {
send({
type: "agent:warning",
message: `${id}: ${e.message}`
});
return [noop, noop];
return [noop, noop, makeDefaultHandlerConfig()];
}
}

Expand Down Expand Up @@ -796,6 +812,12 @@ async function getHandlers(request: HandlerRequest): Promise<HandlerResponse> {
};
}

function makeDefaultHandlerConfig(): HandlerConfig {
return {
capture_backtraces: false,
};
}

function receiveResponse<T>(type: string): Promise<T> {
return new Promise(resolve => {
recv(type, (response: T) => {
Expand Down Expand Up @@ -981,16 +1003,20 @@ interface HandlerResponse {
scripts: HandlerScript[];
}
type HandlerScript = string;
interface HandlerConfig {
capture_backtraces: boolean;
}

type TraceTargetId = number;
type TraceEvent = [TraceTargetId, Timestamp, ThreadId, Depth, Caller, Message];
type TraceEvent = [TraceTargetId, Timestamp, ThreadId, Depth, Caller, Backtrace, Message];

type Timestamp = number;
type Depth = number;
type Caller = string | null;
type Backtrace = string[] | null;
type Message = string;

type TraceHandler = [TraceEnterHandler, TraceLeaveHandler];
type TraceHandler = [onEnter: TraceEnterHandler, onLeave: TraceLeaveHandler, config: HandlerConfig];
type TraceEnterHandler = (log: LogHandler, args: any[], state: TraceState) => void;
type TraceLeaveHandler = (log: LogHandler, retval: any, state: TraceState) => any;

Expand All @@ -1003,7 +1029,8 @@ const agent = new Agent();
rpc.exports = {
init: agent.init.bind(agent),
dispose: agent.dispose.bind(agent),
update: agent.update.bind(agent),
updateHandlerCode: agent.updateHandlerCode.bind(agent),
updateHandlerConfig: agent.updateHandlerConfig.bind(agent),
stageTargets: agent.stageTargets.bind(agent),
commitTargets: agent.commitTargets.bind(agent),
readMemory: agent.readMemory.bind(agent),
Expand Down
11 changes: 9 additions & 2 deletions apps/tracer/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
Tabs,
Tab,
} from "@blueprintjs/core";
import { useState } from "react";
import { useRef, useState } from "react";
import { Resplit } from "react-resplit";

export default function App() {
Expand All @@ -33,6 +33,8 @@ export default function App() {
draftedCode,
setDraftedCode,
deployCode,
captureBacktraces,
setCaptureBacktraces,

events,
latestMatchingEventIndex,
Expand All @@ -46,6 +48,7 @@ export default function App() {
stagedItems,
commitItems,
} = useModel();
const captureBacktracesSwitchRef = useRef<HTMLInputElement>(null);
const [selectedTabId, setSelectedTabId] = useState("events");
const [disassemblyTarget, setDisassemblyTarget] = useState<DisassemblyTarget>();

Expand Down Expand Up @@ -131,7 +134,11 @@ export default function App() {
Disassemble
</Button>
</ButtonGroup>
<Switch>
<Switch
inputRef={captureBacktracesSwitchRef}
checked={captureBacktraces}
onChange={() => setCaptureBacktraces(captureBacktracesSwitchRef.current!.checked)}
>
Capture Backtraces
</Switch>
</section>
Expand Down
15 changes: 9 additions & 6 deletions apps/tracer/src/EventView.css
Original file line number Diff line number Diff line change
Expand Up @@ -52,20 +52,23 @@
color: #1e1e1e;
}

.event-details td {
padding: 5px;
}

.event-details td:nth-child(1) {
vertical-align: top;
text-align: right;
font-weight: bold;
}

.event-details td:nth-child(2) {
padding-left: 6px;
}

.event-details td:nth-child(3) {
padding-left: 10px;
display: flex;
flex-direction: column;
gap: 4px;
}

.event-details .bp5-button {
padding: 5px 10px;
font-size: 10px;
}

Expand Down
18 changes: 13 additions & 5 deletions apps/tracer/src/EventView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export default function EventView({ events, highlightedIndex = null, onActivate,
return (
<div ref={containerRef} className="event-view">
{
events.reduce((result, [targetId, timestamp, threadId, depth, caller, message, style], i) => {
events.reduce((result, [targetId, timestamp, threadId, depth, caller, backtrace, message, style], i) => {
let timestampStr = timestamp.toString();
const timestampPaddingNeeded = Math.max(6 - timestampStr.length, 0);
for (let i = 0; i !== timestampPaddingNeeded; i++) {
Expand Down Expand Up @@ -94,19 +94,27 @@ export default function EventView({ events, highlightedIndex = null, onActivate,
<td>
</td>
</tr>
{(caller !== null) ? (
{(caller !== null && backtrace === null) ? (
<tr>
<td>Caller</td>
<td>{caller}</td>
<td>
<Button icon="code" onClick={() => onDisassemble(caller)}>Disassemble</Button>
<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" onClick={() => onDeactivate(targetId, i)}>Dismiss</Button>
<Button className="event-dismiss" intent="primary" onClick={() => onDeactivate(targetId, i)}>Dismiss</Button>
</Card>
) : null}
</div>
Expand Down
24 changes: 23 additions & 1 deletion apps/tracer/src/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export function useModel() {
const [selectedHandler, setSelectedHandler] = useState<Handler | null>(null);
const [handlerCode, setHandlerCode] = useState("");
const [draftedCode, setDraftedCode] = useState("");
const [captureBacktraces, _setCaptureBacktraces] = useState(false);
const [events, setEvents] = useState<Event[]>([]);
const [latestMatchingEventIndex, setLatestMatchingEventIndex] = useState<number | null>(null);
const [highlightedEventIndex, setHighlightedEventIndex] = useState<number | null>(null);
Expand Down Expand Up @@ -58,6 +59,7 @@ export function useModel() {
const handler = handlers.find(h => h.id === id)!;
setSelectedScope(handler.scope);
setSelectedHandler(handler);
_setCaptureBacktraces(false);
setHighlightedEventIndex(null);
sendJsonMessage({ type: "handler:load", id });
}
Expand All @@ -68,6 +70,17 @@ export function useModel() {
sendJsonMessage({ type: "handler:save", id: selectedHandler!.id, code });
}

function setCaptureBacktraces(enabled: boolean) {
_setCaptureBacktraces(enabled);
sendJsonMessage({
type: "handler:configure",
id: selectedHandler!.id,
parameters: {
capture_backtraces: enabled
}
});
}

function startAddingTargets() {
setAddingTargets(true);
}
Expand Down Expand Up @@ -108,9 +121,10 @@ export function useModel() {
setHandlers(handlers.concat(lastJsonMessage.handlers));
break;
case "handler:loaded": {
const { code } = lastJsonMessage;
const { code, config } = lastJsonMessage;
setHandlerCode(code);
setDraftedCode(code);
_setCaptureBacktraces(config.capture_backtraces);
break;
}
case "targets:staged":
Expand Down Expand Up @@ -159,6 +173,8 @@ export function useModel() {
draftedCode,
setDraftedCode,
deployCode,
captureBacktraces,
setCaptureBacktraces,

events,
latestMatchingEventIndex,
Expand Down Expand Up @@ -194,6 +210,10 @@ export interface Handler {
export type HandlerId = number;
export type ScopeId = string;

interface HandlerConfig {
capture_backtraces: boolean;
}

export type StagedItem = [id: StagedItemId, scope: ScopeName, member: MemberName];
export type StagedItemId = number;

Expand All @@ -206,6 +226,7 @@ export type Event = [
threadId: number,
depth: number,
caller: string | null,
backtrace: string[] | null,
message: string,
style: string[]
];
Expand Down Expand Up @@ -254,6 +275,7 @@ interface HandlersAddMessage {
interface HandlerLoadedMessage {
type: "handler:loaded";
code: string;
config: HandlerConfig;
}

interface TargetsStagedMessage {
Expand Down
Loading

0 comments on commit 71b3a3f

Please sign in to comment.