[Profiling] Simplify query post-processing and flamegraph response (#141729)

* Replace non-null assertion with nullish coalescing

* Remove createFrameGroup

* Remove callers

* Use adjacency list representation for tree

* Move frame type map outside function

* Inline frame group name

* Replace FrameGroupID with ID

* Create columnar view model in client
This commit is contained in:
Joseph Crail 2022-09-26 10:27:06 -07:00 committed by GitHub
parent 90b9afdcee
commit 924c7f912a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 548 additions and 489 deletions

View file

@ -8,12 +8,8 @@
import { schema } from '@kbn/config-schema';
import { RouteRegisterParameters } from '.';
import { getRoutePaths } from '../../common';
import { createCallerCalleeGraph } from '../../common/callercallee';
import {
createColumnarCallerCallee,
createFlameGraph,
ElasticFlameGraph,
} from '../../common/flamegraph';
import { createCalleeTree } from '../../common/callee';
import { createFlameGraph } from '../../common/flamegraph';
import { createProfilingEsClient } from '../utils/create_profiling_es_client';
import { withProfilingSpan } from '../utils/with_profiling_span';
import { getClient } from './compat';
@ -46,55 +42,57 @@ export function registerFlameChartSearchRoute({ router, logger }: RouteRegisterP
});
const totalSeconds = timeTo - timeFrom;
const { stackTraces, executables, stackFrames, eventsIndex, totalCount, stackTraceEvents } =
await getExecutablesAndStackTraces({
logger,
client: createProfilingEsClient({ request, esClient }),
filter,
sampleSize: targetSampleSize,
});
const {
stackTraces,
executables,
stackFrames,
eventsIndex,
totalCount,
totalFrames,
stackTraceEvents,
} = await getExecutablesAndStackTraces({
logger,
client: createProfilingEsClient({ request, esClient }),
filter,
sampleSize: targetSampleSize,
});
const flamegraph = await withProfilingSpan('create_flamegraph', async () => {
const t0 = Date.now();
const graph = createCallerCalleeGraph(
const tree = createCalleeTree(
stackTraceEvents,
stackTraces,
stackFrames,
executables
executables,
totalFrames
);
logger.info(`creating caller-callee graph took ${Date.now() - t0} ms`);
logger.info(`creating callee tree took ${Date.now() - t0} ms`);
// sampleRate is 1/5^N, with N being the downsampled index the events were fetched from.
// N=0: full events table (sampleRate is 1)
// N=1: downsampled by 5 (sampleRate is 0.2)
// ...
// totalCount is the sum(Count) of all events in the filter range in the
// downsampled index we were looking at.
// To estimate how many events we have in the full events index: totalCount / sampleRate.
// Do the same for single entries in the events array.
const t1 = Date.now();
const columnar = createColumnarCallerCallee(graph);
logger.info(`creating columnar caller-callee graph took ${Date.now() - t1} ms`);
const t2 = Date.now();
const fg = createFlameGraph(columnar);
logger.info(`creating flamegraph took ${Date.now() - t2} ms`);
const fg = createFlameGraph(
tree,
totalSeconds,
Math.floor(totalCount / eventsIndex.sampleRate),
totalCount
);
logger.info(`creating flamegraph took ${Date.now() - t1} ms`);
return fg;
});
// sampleRate is 1/5^N, with N being the downsampled index the events were fetched from.
// N=0: full events table (sampleRate is 1)
// N=1: downsampled by 5 (sampleRate is 0.2)
// ...
// totalCount is the sum(Count) of all events in the filter range in the
// downsampled index we were looking at.
// To estimate how many events we have in the full events index: totalCount / sampleRate.
// Do the same for single entries in the events array.
const body: ElasticFlameGraph = {
...flamegraph,
TotalSeconds: totalSeconds,
TotalTraces: Math.floor(totalCount / eventsIndex.sampleRate),
SampledTraces: totalCount,
};
logger.info('returning payload response to client');
return response.ok({ body });
return response.ok({ body: flamegraph });
} catch (e) {
logger.error(e);
return response.customError({

View file

@ -68,7 +68,7 @@ export async function getExecutablesAndStackTraces({
stackTraceEvents.set(id, Math.floor(count / (eventsIndex.sampleRate * p)));
}
const { stackTraces, stackFrameDocIDs, executableDocIDs } = await mgetStackTraces({
const { stackTraces, totalFrames, stackFrameDocIDs, executableDocIDs } = await mgetStackTraces({
logger,
client,
events: stackTraceEvents,
@ -86,6 +86,7 @@ export async function getExecutablesAndStackTraces({
stackFrames,
stackTraceEvents,
totalCount,
totalFrames,
eventsIndex,
};
});

View file

@ -18,6 +18,8 @@ import {
ProfilingStackTrace,
} from '../../common/elasticsearch';
import {
emptyExecutable,
emptyStackFrame,
Executable,
FileID,
StackFrame,
@ -310,7 +312,7 @@ export async function mgetStackTraces({
);
}
return { stackTraces, stackFrameDocIDs, executableDocIDs };
return { stackTraces, totalFrames, stackFrameDocIDs, executableDocIDs };
}
export async function mgetStackFrames({
@ -352,13 +354,7 @@ export async function mgetStackFrames({
});
framesFound++;
} else {
stackFrames.set(frame._id, {
FileName: '',
FunctionName: '',
FunctionOffset: 0,
LineNumber: 0,
SourceType: 0,
});
stackFrames.set(frame._id, emptyStackFrame);
}
}
logger.info(`processing data took ${Date.now() - t0} ms`);
@ -403,9 +399,7 @@ export async function mgetExecutables({
});
exeFound++;
} else {
executables.set(exe._id, {
FileName: '',
});
executables.set(exe._id, emptyExecutable);
}
}
logger.info(`processing data took ${Date.now() - t0} ms`);