Skip to content

Commit

Permalink
Defer trace output display
Browse files Browse the repository at this point in the history
  • Loading branch information
Your Name committed Sep 19, 2024
1 parent 22e100c commit 328b613
Show file tree
Hide file tree
Showing 7 changed files with 167 additions and 89 deletions.
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "frida-cshell",
"version": "1.4.5",
"version": "1.4.6",
"description": "Frida's CShell",
"scripts": {
"prepare": "npm run build && npm run version && npm run package && npm run copy",
Expand Down
29 changes: 24 additions & 5 deletions src/breakpoints/bp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { Parser } from '../io/parser.js';
import { Regs } from './regs.js';
import { Format } from '../misc/format.js';
import { BlockTrace } from '../traces/block.js';
import { Trace, Traces } from '../traces/trace.js';
import { Trace, TraceData, Traces } from '../traces/trace.js';
import { Var } from '../vars/var.js';
import { Vars } from '../vars/vars.js';
import { CallTrace } from '../traces/call.js';
Expand Down Expand Up @@ -210,11 +210,8 @@ export class Bp {
if (this._trace === null) return;
this._trace.stop();

Output.writeln(Output.blue('-'.repeat(80)));
this._trace.display();
Output.writeln(Output.blue('-'.repeat(80)));
Output.clearLine();

Output.writeln(Output.blue('-'.repeat(80)));
Output.writeln(
[
`${Output.yellow('|')} Stop Trace`,
Expand All @@ -227,13 +224,35 @@ export class Bp {
);
Output.writeln(Output.yellow('-'.repeat(80)));

const data = this._trace.data();
setTimeout(() => this.displayTraceData(data));

Traces.delete(threadId);
} finally {
Input.prompt();
Regs.clear();
}
}

private displayTraceData(trace: TraceData) {
Output.clearLine();
Output.writeln(Output.yellow('-'.repeat(80)));
Output.writeln(`${Output.yellow('|')} Displaying trace:`);
Output.writeln(Output.yellow('-'.repeat(80)));
Input.suppressIntercept(true);
Output.setIndent(true);
Output.writeln();
try {
trace.display();
Output.writeln();
} finally {
Output.setIndent(false);
Input.suppressIntercept(false);
Output.writeln(Output.yellow('-'.repeat(80)));
Input.prompt();
}
}

private breakCode(
threadId: ThreadId,
ctx: CpuContext,
Expand Down
86 changes: 51 additions & 35 deletions src/traces/block.ts
Original file line number Diff line number Diff line change
@@ -1,45 +1,20 @@
import { Output } from '../io/output.js';
import { Trace, Traces } from './trace.js';
import { Trace, TraceData, Traces } from './trace.js';

export class BlockTrace implements Trace {
class BlockTraceData implements TraceData {
private static readonly MAX_BLOCKS = 1024;
private threadId: ThreadId;
private trace: ArrayBuffer = new ArrayBuffer(0);
private depth: number;

private constructor(threadId: ThreadId, depth: number, unique: boolean) {
this.threadId = threadId;
public constructor(depth: number) {
this.depth = depth;
Stalker.follow(threadId, {
events: {
call: true,
ret: true,
block: !unique,
compile: unique,
},
onReceive: (events: ArrayBuffer) => {
const newBuffer = new Uint8Array(
this.trace.byteLength + events.byteLength,
);
newBuffer.set(new Uint8Array(this.trace), 0);
newBuffer.set(new Uint8Array(events), this.trace.byteLength);
this.trace = newBuffer.buffer as ArrayBuffer;
},
});
}

public static create(
threadId: ThreadId,
depth: number,
unique: boolean = false,
): BlockTrace {
if (Traces.has(threadId)) {
throw new Error(`trace already exists for threadId: ${threadId}`);
}

const trace = new BlockTrace(threadId, depth, unique);
Traces.set(threadId, trace);
return trace;
public append(events: ArrayBuffer) {
const newBuffer = new Uint8Array(this.trace.byteLength + events.byteLength);
newBuffer.set(new Uint8Array(this.trace), 0);
newBuffer.set(new Uint8Array(events), this.trace.byteLength);
this.trace = newBuffer.buffer as ArrayBuffer;
}

public display() {
Expand All @@ -55,15 +30,15 @@ export class BlockTrace implements Trace {
switch (e.length) {
case 3: {
const [kind, start, _end] = e;
if (currentDepth >= this.depth) break;
if (!first && currentDepth >= this.depth) break;
if (kind !== 'block' && kind !== 'compile') break;
const name = Traces.getAddressString(start as NativePointer);
if (name === null) break;
if (first) {
currentDepth = 0;
first = false;
}
if (numOutput >= BlockTrace.MAX_BLOCKS) {
if (numOutput >= BlockTraceData.MAX_BLOCKS) {
Output.writeln(Output.red(`TRACE TRUNCATED`));
return;
}
Expand All @@ -89,9 +64,50 @@ export class BlockTrace implements Trace {
}
}
}
}

export class BlockTrace implements Trace {
private threadId: ThreadId;
private trace: BlockTraceData;

private constructor(threadId: ThreadId, depth: number, unique: boolean) {
this.threadId = threadId;
this.trace = new BlockTraceData(depth);
Stalker.follow(threadId, {
events: {
call: true,
ret: true,
block: !unique,
compile: unique,
},
onReceive: (events: ArrayBuffer) => {
if (this.trace !== null) {
this.trace.append(events);
}
},
});
}

public static create(
threadId: ThreadId,
depth: number,
unique: boolean = false,
): BlockTrace {
if (Traces.has(threadId)) {
throw new Error(`trace already exists for threadId: ${threadId}`);
}

const trace = new BlockTrace(threadId, depth, unique);
Traces.set(threadId, trace);
return trace;
}

public stop() {
Stalker.unfollow(this.threadId);
Stalker.flush();
}

public data(): BlockTraceData {
return this.trace;
}
}
74 changes: 45 additions & 29 deletions src/traces/call.ts
Original file line number Diff line number Diff line change
@@ -1,39 +1,20 @@
import { Output } from '../io/output.js';
import { Trace, Traces } from './trace.js';
import { Trace, TraceData, Traces } from './trace.js';

export class CallTrace implements Trace {
class CallTraceData implements TraceData {
private static readonly MAX_CALLS = 1024;
private threadId: ThreadId;
private trace: ArrayBuffer = new ArrayBuffer(0);
private depth: number;

private constructor(threadId: ThreadId, depth: number) {
this.threadId = threadId;
public constructor(depth: number) {
this.depth = depth;
Stalker.follow(threadId, {
events: {
call: true,
ret: true,
},
onReceive: (events: ArrayBuffer) => {
const newBuffer = new Uint8Array(
this.trace.byteLength + events.byteLength,
);
newBuffer.set(new Uint8Array(this.trace), 0);
newBuffer.set(new Uint8Array(events), this.trace.byteLength);
this.trace = newBuffer.buffer as ArrayBuffer;
},
});
}

public static create(threadId: ThreadId, depth: number): CallTrace {
if (Traces.has(threadId)) {
throw new Error(`trace already exists for threadId: ${threadId}`);
}

const trace = new CallTrace(threadId, depth);
Traces.set(threadId, trace);
return trace;
public append(events: ArrayBuffer) {
const newBuffer = new Uint8Array(this.trace.byteLength + events.byteLength);
newBuffer.set(new Uint8Array(this.trace), 0);
newBuffer.set(new Uint8Array(events), this.trace.byteLength);
this.trace = newBuffer.buffer as ArrayBuffer;
}

public display() {
Expand All @@ -49,7 +30,7 @@ export class CallTrace implements Trace {
const [kind, from, to, _depth] = e;
if (kind === 'call') {
currentDepth += 1;
if (currentDepth >= this.depth) continue;
if (!first && currentDepth >= this.depth) continue;
const toName = Traces.getAddressString(to as NativePointer);
if (toName === null) continue;

Expand All @@ -61,7 +42,7 @@ export class CallTrace implements Trace {
currentDepth = 1;
first = false;
}
if (numOutput >= CallTrace.MAX_CALLS) {
if (numOutput >= CallTraceData.MAX_CALLS) {
Output.writeln(Output.red(`TRACE TRUNCATED`));
return;
}
Expand All @@ -76,9 +57,44 @@ export class CallTrace implements Trace {
}
}
}
}

export class CallTrace implements Trace {
private threadId: ThreadId;
private trace: CallTraceData;

private constructor(threadId: ThreadId, depth: number) {
this.threadId = threadId;
this.trace = new CallTraceData(depth);
Stalker.follow(threadId, {
events: {
call: true,
ret: true,
},
onReceive: (events: ArrayBuffer) => {
if (this.trace !== null) {
this.trace.append(events);
}
},
});
}

public static create(threadId: ThreadId, depth: number): CallTrace {
if (Traces.has(threadId)) {
throw new Error(`trace already exists for threadId: ${threadId}`);
}

const trace = new CallTrace(threadId, depth);
Traces.set(threadId, trace);
return trace;
}

public stop() {
Stalker.unfollow(this.threadId);
Stalker.flush();
}

public data(): CallTraceData {
return this.trace;
}
}
37 changes: 25 additions & 12 deletions src/traces/coverage/trace.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,33 @@
import { Output } from '../../io/output.js';
import { Trace, Traces } from '../trace.js';
import { Trace, TraceData, Traces } from '../trace.js';
import { Coverage, CoverageSession } from './coverage.js';

export class CoverageTrace implements Trace {
private threadId: ThreadId;
class CoverageTraceData implements TraceData {
private filename: string;
private threadId: ThreadId;

public constructor(threadId: ThreadId, filename: string) {
this.threadId = threadId;
this.filename = filename;
}

public display() {
Output.writeln(
`Wrote coverage for thread: ${Output.yellow(this.threadId.toString())} to: ${Output.green(this.filename)}`,
);
}
}
export class CoverageTrace implements Trace {
private file: File;
private coverage: CoverageSession;
private stopped: boolean = false;
private trace: CoverageTraceData;

private constructor(threadId: ThreadId) {
this.threadId = threadId;
this.filename = CoverageTrace.getRandomFileName();
this.file = new File(this.filename, 'wb+');
const filename = CoverageTrace.getRandomFileName();
this.trace = new CoverageTraceData(threadId, filename);

this.file = new File(filename, 'wb+');
this.coverage = Coverage.start({
moduleFilter: m => Coverage.allModules(m),
onCoverage: coverageData => {
Expand All @@ -32,12 +47,6 @@ export class CoverageTrace implements Trace {
return trace;
}

public display() {
Output.writeln(
`Wrote coverage for thread: ${Output.yellow(this.threadId.toString())} to: ${Output.green(this.filename)}`,
);
}

public stop() {
if (this.stopped) return;
this.coverage.stop();
Expand All @@ -61,4 +70,8 @@ export class CoverageTrace implements Trace {
const filename = `/tmp/${rand}.cov`;
return filename;
}

public data(): CoverageTraceData {
return this.trace;
}
}
Loading

0 comments on commit 328b613

Please sign in to comment.