Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
136 changes: 77 additions & 59 deletions ui/src/components/details/args.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,69 +16,98 @@ import m from 'mithril';
import {isString} from '../../base/object_utils';
import {Icons} from '../../base/semantic_icons';
import {exists} from '../../base/utils';
import {ArgNode, convertArgsToTree, Key} from './slice_args_parser';
import {Anchor} from '../../widgets/anchor';
import {MenuItem, PopupMenu} from '../../widgets/menu';
import {TreeNode} from '../../widgets/tree';
import {Arg} from '../sql_utils/args';
import {Args, ArgsDict, ArgValue} from '../sql_utils/args';
import {Trace} from '../../public/trace';

// Renders slice arguments (key/value pairs) as a subtree.
export function renderArguments(
trace: Trace,
args: ReadonlyArray<Arg>,
extraMenuItems?: (arg: Arg) => m.Children,
args: ArgsDict,
extraMenuItems?: (key: string, arg: ArgValue) => m.Children,
): m.Children {
if (args.length > 0) {
const tree = convertArgsToTree(args);
return renderArgTreeNodes(trace, tree, extraMenuItems);
} else {
return undefined;
if (hasArgs(args)) {
return Object.entries(args).map(([key, value]) =>
renderArgsTree(trace, key, key, value, extraMenuItems),
);
}
return undefined;
}

export function hasArgs(args?: Arg[]): args is Arg[] {
return exists(args) && args.length > 0;
export function hasArgs(args?: ArgsDict): args is ArgsDict {
return exists(args) && Object.keys(args).length > 0;
}

function renderArgTreeNodes(
function renderArgsTree(
trace: Trace,
args: ArgNode<Arg>[],
extraMenuItems?: (arg: Arg) => m.Children,
key: string,
fullKey: string,
args: Args,
extraMenuItems?: (path: string, arg: ArgValue) => m.Children,
): m.Children {
return args.map((arg) => {
const {key, value, children} = arg;
if (children && children.length === 1) {
// If we only have one child, collapse into self and combine keys
const child = children[0];
const compositeArg = {
...child,
key: stringifyKey(key, child.key),
};
return renderArgTreeNodes(trace, [compositeArg], extraMenuItems);
} else {
return m(
TreeNode,
{
left: renderArgKey(stringifyKey(key), value, extraMenuItems),
right: exists(value) && renderArgValue(value),
summary: children && renderSummary(children),
},
children && renderArgTreeNodes(trace, children, extraMenuItems),
if (args instanceof Array) {
return m(
TreeNode,
{
left: key,
summary: renderArraySummary(args),
},
args.map((value, index) =>
renderArgsTree(
trace,
`[${index}]`,
`${fullKey}[${index}]`,
value,
extraMenuItems,
),
),
);
}
if (args !== null && typeof args === 'object') {
if (Object.keys(args).length === 1) {
const [[childName, value]] = Object.entries(args);
return renderArgsTree(
trace,
`${key}.${childName}`,
`${fullKey}.${childName}`,
value,
extraMenuItems,
);
}
return m(
TreeNode,
{
left: key,
summary: renderDictSummary(args),
},
Object.entries(args).map(([childName, child]) =>
renderArgsTree(
trace,
childName,
`${fullKey}.${childName}`,
child,
extraMenuItems,
),
),
);
}
return m(TreeNode, {
left: renderArgKey(key, fullKey, args, extraMenuItems),
right: renderArgValue(args),
});
}

function renderArgKey(
key: string,
value: Arg | undefined,
extraMenuItems?: (arg: Arg) => m.Children,
fullKey: string,
value: ArgValue,
extraMenuItems?: (path: string, arg: ArgValue) => m.Children,
): m.Children {
if (value === undefined) {
return key;
} else {
const {key: fullKey} = value;
return m(
PopupMenu,
{trigger: m(Anchor, {icon: Icons.ContextMenu}, key)},
Expand All @@ -87,44 +116,33 @@ function renderArgKey(
icon: 'content_copy',
onclick: () => navigator.clipboard.writeText(fullKey),
}),
extraMenuItems?.(value),
extraMenuItems?.(fullKey, value),
);
}
}

function renderArgValue({displayValue}: Arg): m.Children {
if (isWebLink(displayValue)) {
return renderWebLink(displayValue);
function renderArgValue(value: ArgValue): m.Children {
if (isWebLink(value)) {
return renderWebLink(value);
} else {
return `${displayValue}`;
return `${value}`;
}
}

function renderSummary(children: ArgNode<Arg>[]): m.Children {
const summary = children
.slice(0, 2)
.map(({key}) => key)
.join(', ');
const remaining = children.length - 2;
function renderArraySummary(children: Args[]): m.Children {
return `[ ... (${children.length} items) ]`;
}

function renderDictSummary(children: ArgsDict): m.Children {
const summary = Object.keys(children).slice(0, 2).join(', ');
const remaining = Object.keys(children).length - 2;
if (remaining > 0) {
return `{${summary}, ... (${remaining} more items)}`;
} else {
return `{${summary}}`;
}
}

function stringifyKey(...key: Key[]): string {
return key
.map((element, index) => {
if (typeof element === 'number') {
return `[${element}]`;
} else {
return (index === 0 ? '' : '.') + element;
}
})
.join('');
}

function isWebLink(value: unknown): value is string {
return (
isString(value) &&
Expand Down
16 changes: 7 additions & 9 deletions ui/src/components/details/slice_args.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

import m from 'mithril';
import {MenuItem} from '../../widgets/menu';
import {Arg} from '../sql_utils/args';
import {ArgsDict} from '../sql_utils/args';
import {Trace} from '../../public/trace';
import {renderArguments} from './args';
import {extensions} from '../extensions';
Expand All @@ -23,11 +23,9 @@ import {getSqlTableDescription} from '../widgets/sql/table/sql_table_registry';
import {sqliteString} from '../../base/string_utils';

// Renders slice arguments (key/value pairs) as a subtree.
export function renderSliceArguments(
trace: Trace,
args: ReadonlyArray<Arg>,
): m.Children {
return renderArguments(trace, args, (arg) => {
export function renderSliceArguments(trace: Trace, args: ArgsDict): m.Children {
return renderArguments(trace, args, (key, value) => {
const displayValue = value === null ? 'NULL' : String(value);
return [
m(MenuItem, {
label: 'Find slices with same arg value',
Expand All @@ -37,15 +35,15 @@ export function renderSliceArguments(
table: assertExists(getSqlTableDescription(trace, 'slice')),
filters: [
{
op: (cols) => `${cols[0]} = ${sqliteString(arg.displayValue)}`,
op: (cols) => `${cols[0]} = ${sqliteString(displayValue)}`,
columns: [
{
column: 'display_value',
source: {
table: 'args',
joinOn: {
arg_set_id: 'arg_set_id',
key: sqliteString(arg.flatKey),
key: sqliteString(key),
},
},
},
Expand All @@ -59,7 +57,7 @@ export function renderSliceArguments(
label: 'Visualize argument values',
icon: 'query_stats',
onclick: () => {
extensions.addVisualizedArgTracks(trace, arg.flatKey);
extensions.addVisualizedArgTracks(trace, key);
},
}),
];
Expand Down
58 changes: 14 additions & 44 deletions ui/src/components/sql_utils/args.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,58 +13,28 @@
// limitations under the License.

import {Engine} from '../../trace_processor/engine';
import {
LONG_NULL,
NUM,
NUM_NULL,
STR,
STR_NULL,
} from '../../trace_processor/query_result';
import {ArgSetId, ArgsId, asArgId} from './core_types';
import {STR_NULL} from '../../trace_processor/query_result';
import {ArgSetId} from './core_types';

export interface Arg {
id: ArgsId;
flatKey: string;
key: string;
displayValue: string;
}
export type ArgValue = string | number | boolean | null;
export type Args = ArgValue | Args[] | ArgsDict;
export type ArgsDict = {[key: string]: Args};

export async function getArgs(
engine: Engine,
argSetId: ArgSetId,
): Promise<Arg[]> {
): Promise<ArgsDict> {
const query = await engine.query(`
SELECT
id,
flat_key as flatKey,
key,
int_value as intValue,
string_value as stringValue,
real_value as realValue,
value_type as valueType,
display_value as displayValue
FROM args
WHERE arg_set_id = ${argSetId}
ORDER BY id`);
SELECT __intrinsic_arg_set_to_json(${argSetId}) as args_json
`);
const it = query.iter({
id: NUM,
flatKey: STR,
key: STR,
intValue: LONG_NULL,
stringValue: STR_NULL,
realValue: NUM_NULL,
valueType: STR,
displayValue: STR_NULL,
args_json: STR_NULL,
});

const result: Arg[] = [];
for (; it.valid(); it.next()) {
result.push({
id: asArgId(it.id),
flatKey: it.flatKey,
key: it.key,
displayValue: it.displayValue ?? 'NULL',
});
if (!it.valid() || it.args_json === null) {
return {};
}
return result;

const argsDict = JSON.parse(it.args_json);
return argsDict;
}
4 changes: 2 additions & 2 deletions ui/src/components/sql_utils/slice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ import {
Upid,
Utid,
} from './core_types';
import {Arg, getArgs} from './args';
import {ArgsDict, getArgs} from './args';
import {getThreadInfo, ThreadInfo} from './thread';
import {getProcessInfo, ProcessInfo} from './process';

Expand All @@ -55,7 +55,7 @@ export interface SliceDetails {
threadTs?: time;
threadDur?: duration;
category?: string;
args?: Arg[];
args?: ArgsDict;
}

async function getUtidAndUpid(
Expand Down
6 changes: 3 additions & 3 deletions ui/src/components/tracks/debug_slice_track_details_panel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ import {TrackEventDetailsPanel} from '../../public/details_panel';
import {Trace} from '../../public/trace';
import {SqlRef} from '../../widgets/sql_ref';
import {renderSliceArguments} from '../details/slice_args';
import {Arg, getArgs} from '../sql_utils/args';
import {ArgsDict, getArgs} from '../sql_utils/args';

export const RAW_PREFIX = 'raw_';

Expand Down Expand Up @@ -67,7 +67,7 @@ export class DebugSliceTrackDetailsPanel implements TrackEventDetailsPanel {

// These are the actual loaded args from the args table assuming an arg_set_id
// is supplied.
private args?: Arg[];
private args?: ArgsDict;

// We will try to interpret the arguments as references into well-known
// tables. These values will be set if the relevant columns exist and
Expand Down Expand Up @@ -260,7 +260,7 @@ export class DebugSliceTrackDetailsPanel implements TrackEventDetailsPanel {
]);
}

private renderArgsSection(args: Arg[]) {
private renderArgsSection(args: ArgsDict) {
return m(Section, {title: 'Arguments'}, [
m(Tree, renderArguments(this.trace, args)),
]);
Expand Down
6 changes: 3 additions & 3 deletions ui/src/components/widgets/sql/details/details.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import {raf} from '../../../../core/raf_scheduler';
import {Engine} from '../../../../trace_processor/engine';
import {Row, SqlValue} from '../../../../trace_processor/query_result';
import {sqlValueToReadableString} from '../../../../trace_processor/sql_utils';
import {Arg, getArgs} from '../../../sql_utils/args';
import {ArgsDict, getArgs} from '../../../sql_utils/args';
import {asArgSetId} from '../../../sql_utils/core_types';
import {Anchor} from '../../../../widgets/anchor';
import {renderError} from '../../../../widgets/error';
Expand Down Expand Up @@ -432,7 +432,7 @@ interface Data {
// Source statements for the arg sets.
argSetExpressions: string[];
// Fetched arg sets.
argSets: (Arg[] | Err)[];
argSets: (ArgsDict | Err)[];

// Source statements for the SQL references.
sqlIdRefs: {tableName: string; idExpression: string}[];
Expand Down Expand Up @@ -508,7 +508,7 @@ class DataController {
for (const argSetIndex of this.argSets) {
const argSetId = data.values[argSetIndex];
if (argSetId === null) {
data.argSets.push([]);
data.argSets.push({});
} else if (typeof argSetId !== 'number' && typeof argSetId !== 'bigint') {
data.argSets.push(
new Err(
Expand Down
Loading