Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Session view] Search will now only search through verbose stuff if verbose mode is on. #129959

Merged
merged 12 commits into from
Apr 12, 2022
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ export const mockEvents: ProcessEvent[] = [
action: EventAction.fork,
category: 'process',
kind: EventKind.event,
id: '1',
},
host: {
architecture: 'x86_64',
Expand Down Expand Up @@ -317,6 +318,7 @@ export const mockEvents: ProcessEvent[] = [
action: EventAction.exec,
category: 'process',
kind: EventKind.event,
id: '2',
},
},
{
Expand Down Expand Up @@ -459,6 +461,7 @@ export const mockEvents: ProcessEvent[] = [
action: EventAction.end,
category: 'process',
kind: EventKind.event,
id: '3',
},
host: {
architecture: 'x86_64',
Expand Down Expand Up @@ -621,6 +624,7 @@ export const mockEvents: ProcessEvent[] = [
action: EventAction.end,
category: 'process',
kind: EventKind.event,
id: '4',
},
host: {
architecture: 'x86_64',
Expand Down Expand Up @@ -809,6 +813,7 @@ export const mockAlerts: ProcessEvent[] = [
action: EventAction.exec,
category: 'process',
kind: EventKind.signal,
id: '5',
},
host: {
architecture: 'x86_64',
Expand Down Expand Up @@ -995,6 +1000,7 @@ export const mockAlerts: ProcessEvent[] = [
action: EventAction.end,
category: 'process',
kind: EventKind.signal,
id: '6',
},
host: {
architecture: 'x86_64',
Expand Down Expand Up @@ -1264,6 +1270,7 @@ export const childProcessMock: Process = {
getAlerts: () => [],
updateAlertsStatus: (_) => undefined,
hasExec: () => false,
isVerbose: () => true,
getOutput: () => '',
getDetails: () =>
({
Expand All @@ -1272,6 +1279,7 @@ export const childProcessMock: Process = {
kind: EventKind.event,
category: 'process',
action: EventAction.exec,
id: '1',
},
host: {
architecture: 'x86_64',
Expand Down Expand Up @@ -1326,6 +1334,7 @@ export const childProcessMock: Process = {
isUserEntered: () => false,
getMaxAlertLevel: () => null,
getEndTime: () => '',
isDescendantOf: () => false,
};

export const processMock: Process = {
Expand All @@ -1346,6 +1355,7 @@ export const processMock: Process = {
getAlerts: () => [],
updateAlertsStatus: (_) => undefined,
hasExec: () => false,
isVerbose: () => true,
getOutput: () => '',
getDetails: () =>
({
Expand All @@ -1354,6 +1364,7 @@ export const processMock: Process = {
kind: EventKind.event,
category: 'process',
action: EventAction.exec,
id: '2',
},
host: {
architecture: 'x86_64',
Expand Down Expand Up @@ -1390,6 +1401,14 @@ export const processMock: Process = {
working_directory: '/home/vagrant',
start: '2021-11-23T15:25:04.210Z',
pid: 1,
user: {
id: '1000',
name: 'vagrant',
},
group: {
id: '1000',
name: 'vagrant',
},
parent: {
pid: 2442,
user: {
Expand Down Expand Up @@ -1499,6 +1518,7 @@ export const processMock: Process = {
isUserEntered: () => false,
getMaxAlertLevel: () => null,
getEndTime: () => '',
isDescendantOf: () => false,
};

export const sessionViewBasicProcessMock: Process = {
Expand Down Expand Up @@ -1544,7 +1564,9 @@ export const mockProcessMap = mockEvents.reduce(
getDetails: () => event,
isUserEntered: () => false,
getMaxAlertLevel: () => null,
isVerbose: () => true,
getEndTime: () => '',
isDescendantOf: () => false,
};
return processMap;
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ export interface ProcessEvent {
kind?: EventKind;
category?: string;
action?: EventAction;
id?: string;
};
user?: User;
group?: Group;
Expand Down Expand Up @@ -178,7 +179,9 @@ export interface Process {
isUserEntered(): boolean;
getMaxAlertLevel(): number | null;
getChildren(verboseMode: boolean): Process[];
isVerbose(): boolean;
getEndTime(): string;
isDescendantOf(process: Process): boolean;
}

export type ProcessMap = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ describe('process tree hook helpers tests', () => {
});

it('searchProcessTree works', () => {
const searchResults = searchProcessTree(mockProcessMap, SEARCH_QUERY);
const searchResults = searchProcessTree(mockProcessMap, SEARCH_QUERY, true);

// search returns the process with search query in its event args
expect(searchResults[0].id).toBe(SEARCH_RESULT_PROCESS_ID);
Expand All @@ -76,7 +76,7 @@ describe('process tree hook helpers tests', () => {
processMap[SESSION_ENTITY_ID].children = childProcesses;

expect(processMap[SESSION_ENTITY_ID].autoExpand).toBeFalsy();
processMap = autoExpandProcessTree(processMap);
processMap = autoExpandProcessTree(processMap, SEARCH_RESULT_PROCESS_ID);
// session leader should have autoExpand to be true
expect(processMap[SESSION_ENTITY_ID].autoExpand).toBeTruthy();
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { sortProcesses } from '../../../common/utils/sort_processes';
import {
AlertStatusEventEntityIdMap,
EventKind,
Expand Down Expand Up @@ -137,13 +138,25 @@ export const buildProcessTree = (
// this funtion also returns a list of process results which is used by session_view_search_bar to drive
// result navigation UX
// FYI: this function mutates properties of models contained in processMap
export const searchProcessTree = (processMap: ProcessMap, searchQuery: string | undefined) => {
export const searchProcessTree = (
processMap: ProcessMap,
searchQuery: string | undefined,
verboseMode: boolean
) => {
const results = [];

for (const processId of Object.keys(processMap)) {
const process = processMap[processId];

if (searchQuery) {
const details = process.getDetails();
const entryLeader = details?.process?.entry_leader;

// if this is the entry leader process OR verbose mode is OFF and is a verbose process, don't match.
if (entryLeader?.entity_id === process.id || (!verboseMode && process.isVerbose())) {
continue;
}

const event = process.getDetails();
const { working_directory: workingDirectory, args } = event.process || {};

Expand All @@ -162,19 +175,19 @@ export const searchProcessTree = (processMap: ProcessMap, searchQuery: string |
}
}

return results;
return results.sort(sortProcesses);
};

// Iterate over all processes in processMap, and mark each process (and it's ancestors) for auto expansion if:
// a) the process was "user entered" (aka an interactive group leader)
// b) matches the plain text search above
// b) we are jumping to a specific process
// Returns the processMap with it's processes autoExpand bool set to true or false
// process.autoExpand is read by process_tree_node to determine whether to auto expand it's child processes.
export const autoExpandProcessTree = (processMap: ProcessMap, jumpToEntityId?: string) => {
for (const processId of Object.keys(processMap)) {
const process = processMap[processId];

if (process.searchMatched || process.isUserEntered() || jumpToEntityId === process.id) {
if (process.isUserEntered() || jumpToEntityId === process.id || process.hasAlerts()) {
let { parent } = process;
const parentIdSet = new Set<string>();

Expand Down
73 changes: 54 additions & 19 deletions x-pack/plugins/session_view/public/components/process_tree/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ interface UseProcessTreeDeps {
alerts: ProcessEvent[];
searchQuery?: string;
updatedAlertsStatus: AlertStatusEventEntityIdMap;
verboseMode: boolean;
jumpToEntityId?: string;
}

Expand All @@ -54,10 +55,16 @@ export class ProcessImpl implements Process {
this.searchMatched = null;
}

addEvent(event: ProcessEvent) {
addEvent(newEvent: ProcessEvent) {
// rather than push new events on the array, we return a new one
// this helps the below memoizeOne functions to behave correctly.
this.events = this.events.concat(event);
const exists = this.events.find((event) => {
return event.event?.id === newEvent.event?.id;
});

if (!exists) {
this.events = this.events.concat(newEvent);
}
}

addAlert(alert: ProcessEvent) {
Expand All @@ -66,44 +73,57 @@ export class ProcessImpl implements Process {

clearSearch() {
this.searchMatched = null;
this.autoExpand = false;
}

getChildren(verboseMode: boolean) {
let children = this.children;

// if there are orphans, we just render them inline with the other child processes (currently only session leader does this)
if (this.orphans.length) {
children = [...children, ...this.orphans].sort(sortProcesses);
children = [...children, ...this.orphans];
}
// When verboseMode is false, we filter out noise via a few techniques.
// This option is driven by the "verbose mode" toggle in SessionView/index.tsx
if (!verboseMode) {
return children.filter((child) => {
children = children.filter((child) => {
if (child.events.length === 0) {
return false;
}

const { group_leader: groupLeader, session_leader: sessionLeader } =
child.getDetails().process ?? {};

// search matches or processes with alerts will never be filtered out
if (child.autoExpand || child.searchMatched || child.hasAlerts()) {
// processes with alerts will never be filtered out
if (child.autoExpand || child.hasAlerts()) {
return true;
}

// Hide processes that have their session leader as their process group leader.
// This accounts for a lot of noise from bash and other shells forking, running auto completion processes and
// other shell startup activities (e.g bashrc .profile etc)
if (!groupLeader || !sessionLeader || groupLeader.pid === sessionLeader.pid) {
if (child.isVerbose()) {
return false;
}

return true;
});
}

return children;
return children.sort(sortProcesses);
}

isVerbose() {
const {
group_leader: groupLeader,
session_leader: sessionLeader,
entry_leader: entryLeader,
} = this.getDetails().process ?? {};

// Processes that have their session leader as their process group leader are considered "verbose"
// This accounts for a lot of noise from bash and other shells forking, running auto completion processes and
// other shell startup activities (e.g bashrc .profile etc)
if (
this.id !== entryLeader?.entity_id &&
(!groupLeader || !sessionLeader || groupLeader.pid === sessionLeader.pid)
) {
return true;
}

return false;
}

hasOutput() {
Expand Down Expand Up @@ -221,6 +241,20 @@ export class ProcessImpl implements Process {
// If a process has an 'end' event will always be returned (since it is last and includes details like exit_code and end time)
return filtered[filtered.length - 1] ?? {};
});

isDescendantOf(process: Process) {
let parent = this.parent;

while (parent) {
if (parent === process) {
return true;
}

parent = parent.parent;
}

return false;
}
}

export const useProcessTree = ({
Expand All @@ -229,6 +263,7 @@ export const useProcessTree = ({
alerts,
searchQuery,
updatedAlertsStatus,
verboseMode,
jumpToEntityId,
}: UseProcessTreeDeps) => {
// initialize map, as well as a placeholder for session leader process
Expand Down Expand Up @@ -289,8 +324,9 @@ export const useProcessTree = ({
setProcessMap({ ...updatedProcessMap });
setProcessedPages([...processedPages, ...newProcessedPages]);
setOrphans(newOrphans);
autoExpandProcessTree(updatedProcessMap, jumpToEntityId);
}
}, [data, processMap, orphans, processedPages, sessionEntityId]);
}, [data, processMap, orphans, processedPages, sessionEntityId, jumpToEntityId]);

useEffect(() => {
// currently we are loading a single page of alerts, with no pagination
Expand All @@ -303,9 +339,8 @@ export const useProcessTree = ({
}, [processMap, alerts, alertsProcessed]);

useEffect(() => {
setSearchResults(searchProcessTree(processMap, searchQuery));
autoExpandProcessTree(processMap, jumpToEntityId);
}, [searchQuery, processMap, jumpToEntityId]);
setSearchResults(searchProcessTree(processMap, searchQuery, verboseMode));
}, [searchQuery, processMap, verboseMode]);

// set new orphans array on the session leader
const sessionLeader = processMap[sessionEntityId];
Expand Down
Loading