mirror of
https://github.com/elastic/kibana.git
synced 2025-06-27 10:40:07 -04:00
[examples] add routes to access v8 profiling (#155956)
Adds routes to run v8 profiling tools, when running the examples plugins via `--run-examples` See the included README.md for more info
This commit is contained in:
parent
b12238bac8
commit
1f3426942c
17 changed files with 547 additions and 0 deletions
1
.github/CODEOWNERS
vendored
1
.github/CODEOWNERS
vendored
|
@ -742,6 +742,7 @@ packages/kbn-utility-types @elastic/kibana-core
|
||||||
packages/kbn-utility-types-jest @elastic/kibana-operations
|
packages/kbn-utility-types-jest @elastic/kibana-operations
|
||||||
packages/kbn-utils @elastic/kibana-operations
|
packages/kbn-utils @elastic/kibana-operations
|
||||||
x-pack/plugins/ux @elastic/uptime
|
x-pack/plugins/ux @elastic/uptime
|
||||||
|
examples/v8_profiler_examples @elastic/response-ops
|
||||||
packages/kbn-validate-next-docs-cli @elastic/kibana-operations
|
packages/kbn-validate-next-docs-cli @elastic/kibana-operations
|
||||||
src/plugins/vis_default_editor @elastic/kibana-visualizations
|
src/plugins/vis_default_editor @elastic/kibana-visualizations
|
||||||
src/plugins/vis_types/gauge @elastic/kibana-visualizations
|
src/plugins/vis_types/gauge @elastic/kibana-visualizations
|
||||||
|
|
112
examples/v8_profiler_examples/README.md
Normal file
112
examples/v8_profiler_examples/README.md
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
# V8 profiler examples
|
||||||
|
|
||||||
|
Provides access to the V8 CPU Profiler and Heap Profiler, to inspect
|
||||||
|
the Kibana running this plugin.
|
||||||
|
|
||||||
|
The endpoints are:
|
||||||
|
|
||||||
|
/_dev/cpu_profile?duration=(seconds)&interval=(microseconds)
|
||||||
|
/_dev/heap_profile?duration=(seconds)&interval=(bytes)
|
||||||
|
|
||||||
|
The default duration is 5 seconds.
|
||||||
|
|
||||||
|
For more information on these V8 APIs, see:
|
||||||
|
|
||||||
|
- https://chromedevtools.github.io/devtools-protocol/v8/Profiler/
|
||||||
|
- https://chromedevtools.github.io/devtools-protocol/v8/HeapProfiler/
|
||||||
|
|
||||||
|
Note that due to bugs or limitations,
|
||||||
|
it's not possible to generate heap snapshots using the techniques used
|
||||||
|
to generate the cpu and heap profiles.
|
||||||
|
|
||||||
|
Try them right now, assuming you started Kibana with `--run-examples`!
|
||||||
|
|
||||||
|
- [`http://localhost:5601/_dev/cpu_profile`](http://localhost:5601/_dev/cpu_profile)
|
||||||
|
- [`http://localhost:5601/_dev/heap_profile`](http://localhost:5601/_dev/heap_profile)
|
||||||
|
|
||||||
|
|
||||||
|
When using curl, you can use the `-kOJ` options, which:
|
||||||
|
|
||||||
|
- `-k --insecure`: allow HTTPS usage with self-signed certs
|
||||||
|
- `-O --remote-name`: use the server-specified name for this download
|
||||||
|
- `-J --remote-header-name`: use the `Content-Disposition` as the name of
|
||||||
|
the download
|
||||||
|
|
||||||
|
So one of the following should work for you, to run a 10 second CPU profile
|
||||||
|
using an interval of 100μs (default: 5s / 1000μs):
|
||||||
|
|
||||||
|
```
|
||||||
|
curl -OJ "http://elastic:changeme@localhost:5601/_dev/cpu_profile?duration=10&interval=100"
|
||||||
|
curl -kOJ "https://elastic:changeme@localhost:5601/_dev/cpu_profile?duration=10&interval=100"
|
||||||
|
```
|
||||||
|
|
||||||
|
The files generated will be:
|
||||||
|
|
||||||
|
MM-DD_hh-mm-ss.cpuprofile
|
||||||
|
MM-DD_hh-mm-ss.heapprofile
|
||||||
|
|
||||||
|
These filetypes are the ones expected by various V8 tools that can read these.
|
||||||
|
|
||||||
|
You can use these URLs in your browser, and the files will be saved with the
|
||||||
|
generated names.
|
||||||
|
|
||||||
|
## profile / heap profile readers
|
||||||
|
|
||||||
|
The traditional tools used to view these are part of Chrome Dev Tools (CDT)
|
||||||
|
and now VS Code also supports viewing these files. They provide
|
||||||
|
similar capabilities.
|
||||||
|
|
||||||
|
There doesn't seem to be a much doc available on how to use the viewers
|
||||||
|
for these files. The Chrome Dev Tools docs are extremely old and appear
|
||||||
|
to be out-of-date with the current user interface. The VS Code
|
||||||
|
documentation [Analyzing a profile][] is more recent, but there's not
|
||||||
|
much there.
|
||||||
|
|
||||||
|
[Analyzing a profile]: https://code.visualstudio.com/docs/nodejs/profiling#_analyzing-a-profile
|
||||||
|
|
||||||
|
For CPU profiles, open CDT and then click on the "Performance" tab. You
|
||||||
|
should be able to drop a file right from Finder / Explorer onto the CDT
|
||||||
|
window, and then get the visualization of the profile. If you
|
||||||
|
downloaded the profile right from the browser, using the URL in the URL
|
||||||
|
bar, you can drop the download file from the download status button
|
||||||
|
right into CDT.
|
||||||
|
|
||||||
|
For heap profiles, open CDT and then click on the "Memory" tab. Drag and
|
||||||
|
drop doesn't seem to work here, but you can load a file via a file
|
||||||
|
prompter by clicking the "Load" button at the bottom of the "Memory"
|
||||||
|
pane. You will probably need to scroll to see the button.
|
||||||
|
|
||||||
|
VSCode now supports `.cpuprofile` files and `.heapprofile` files
|
||||||
|
directly, displaying them as a table of function timings. There is also
|
||||||
|
[an extension available to display flame charts][] installed by clicking
|
||||||
|
on the grey-ed out "flame" button on the top-right of the cpu profile
|
||||||
|
view.
|
||||||
|
|
||||||
|
[an extension available to display flame charts]: https://marketplace.visualstudio.com/items?itemName=ms-vscode.vscode-js-profile-flame
|
||||||
|
|
||||||
|
There seem to be problems with both CDT and the VS Code tools, at times.
|
||||||
|
The flame charts in VS Code seem to go haywire sometimes. The heap
|
||||||
|
profile tables in CDT don't expand. Etc. So, beware, and be prepared to
|
||||||
|
have to use multiple tools to analyze these files.
|
||||||
|
|
||||||
|
An alternate view of CPU profiles, which organizes files based on
|
||||||
|
"packages", is available with the **NO**de **PRO**filer (`no-pro`) thing
|
||||||
|
available at https://pmuellr.github.io/no-pro/ . It also supports
|
||||||
|
drag-n-drop of CPU profile files. Note that you can get more
|
||||||
|
directories to show up as "packages", by bringing up CDT and running the
|
||||||
|
following code:
|
||||||
|
|
||||||
|
localStorage['fake-packages-dirs'] = "x-pack/plugins,packages,src/core"
|
||||||
|
|
||||||
|
|
||||||
|
## tips / tricks
|
||||||
|
|
||||||
|
If you're handy with Mac Finder, or other ways of auto-launching apps
|
||||||
|
based on file extensions, it's easy to associate `.cpuprofile` files
|
||||||
|
with vscode.
|
||||||
|
|
||||||
|
Since the http endpoints are GET requests, they are easy to bookmark.
|
||||||
|
Start a profile by clicking on a bookmark.
|
||||||
|
|
||||||
|
When these files are downloaded via Chrome, you can typically launch
|
||||||
|
them directly from the download bar, or drag the file to a viewer.
|
12
examples/v8_profiler_examples/kibana.jsonc
Normal file
12
examples/v8_profiler_examples/kibana.jsonc
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"type": "plugin",
|
||||||
|
"id": "@kbn/v8-profiler-examples-plugin",
|
||||||
|
"owner": "@elastic/response-ops",
|
||||||
|
"description": "Provides access to the v8 cpu profiler and heap profiler running this app",
|
||||||
|
"plugin": {
|
||||||
|
// umm, vs code says I can't use digits in the id field?
|
||||||
|
"id": "vEIGHTProfilerExamples",
|
||||||
|
"server": true,
|
||||||
|
"browser": false,
|
||||||
|
}
|
||||||
|
}
|
14
examples/v8_profiler_examples/server/index.ts
Normal file
14
examples/v8_profiler_examples/server/index.ts
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License
|
||||||
|
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||||
|
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||||
|
* Side Public License, v 1.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { PluginInitializerContext } from '@kbn/core/server';
|
||||||
|
import { V8ProfilerExamplesPlugin } from './plugin';
|
||||||
|
|
||||||
|
export function plugin(initializerContext: PluginInitializerContext) {
|
||||||
|
return new V8ProfilerExamplesPlugin(initializerContext);
|
||||||
|
}
|
34
examples/v8_profiler_examples/server/lib/cpu_profile.ts
Normal file
34
examples/v8_profiler_examples/server/lib/cpu_profile.ts
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License
|
||||||
|
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||||
|
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||||
|
* Side Public License, v 1.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Session } from './session';
|
||||||
|
|
||||||
|
interface StartProfilingArgs {
|
||||||
|
interval: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start a new profile, resolves to a function to stop the profile and resolve
|
||||||
|
// the profile data.
|
||||||
|
export async function startProfiling(
|
||||||
|
session: Session,
|
||||||
|
args: StartProfilingArgs
|
||||||
|
): Promise<() => any> {
|
||||||
|
session.logger.info(`starting cpu profile with args: ${JSON.stringify(args)}`);
|
||||||
|
|
||||||
|
await session.post('Profiler.enable');
|
||||||
|
// microseconds, v8 default is 1000
|
||||||
|
await session.post('Profiler.setSamplingInterval', args);
|
||||||
|
await session.post('Profiler.start');
|
||||||
|
|
||||||
|
// returned function which stops the profile and resolves to the profile data
|
||||||
|
return async function stopProfiling() {
|
||||||
|
session.logger.info('stopping cpu profile');
|
||||||
|
const result: any = await session.post('Profiler.stop');
|
||||||
|
return result.profile;
|
||||||
|
};
|
||||||
|
}
|
27
examples/v8_profiler_examples/server/lib/deferred.ts
Normal file
27
examples/v8_profiler_examples/server/lib/deferred.ts
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License
|
||||||
|
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||||
|
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||||
|
* Side Public License, v 1.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export function createDeferred() {
|
||||||
|
let resolver: any;
|
||||||
|
let rejecter: any;
|
||||||
|
|
||||||
|
function resolve(...args: any[]) {
|
||||||
|
resolver(...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
function reject(...args: any[]) {
|
||||||
|
rejecter(...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
const promise = new Promise((resolve_, reject_) => {
|
||||||
|
resolver = resolve_;
|
||||||
|
rejecter = reject_;
|
||||||
|
});
|
||||||
|
|
||||||
|
return { promise, resolve, reject };
|
||||||
|
}
|
35
examples/v8_profiler_examples/server/lib/heap_profile.ts
Normal file
35
examples/v8_profiler_examples/server/lib/heap_profile.ts
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License
|
||||||
|
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||||
|
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||||
|
* Side Public License, v 1.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Session } from './session';
|
||||||
|
|
||||||
|
interface StartProfilingArgs {
|
||||||
|
samplingInterval: number;
|
||||||
|
includeObjectsCollectedByMajorGC: boolean;
|
||||||
|
includeObjectsCollectedByMinorGC: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start a new profile, resolves to a function to stop the profile and resolve
|
||||||
|
// the profile data.
|
||||||
|
export async function startProfiling(
|
||||||
|
session: Session,
|
||||||
|
args: StartProfilingArgs
|
||||||
|
): Promise<() => any> {
|
||||||
|
session.logger.info(`starting heap profile with args: ${JSON.stringify(args)}`);
|
||||||
|
|
||||||
|
await session.post('Profiler.enable');
|
||||||
|
await session.post('HeapProfiler.enable');
|
||||||
|
await session.post('HeapProfiler.startSampling', args);
|
||||||
|
|
||||||
|
// returned function which stops the profile and resolves to the profile data
|
||||||
|
return async function stopProfiling() {
|
||||||
|
session.logger.info('stopping heap profile');
|
||||||
|
const result: any = await session.post('HeapProfiler.stopSampling');
|
||||||
|
return result.profile;
|
||||||
|
};
|
||||||
|
}
|
79
examples/v8_profiler_examples/server/lib/session.ts
Normal file
79
examples/v8_profiler_examples/server/lib/session.ts
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License
|
||||||
|
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||||
|
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||||
|
* Side Public License, v 1.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Logger } from '@kbn/core/server';
|
||||||
|
|
||||||
|
import { createDeferred } from './deferred';
|
||||||
|
|
||||||
|
let inspector: any = null;
|
||||||
|
try {
|
||||||
|
inspector = require('inspector');
|
||||||
|
} catch (err) {
|
||||||
|
// inspector will be null :-(
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function createSession(logger: Logger): Promise<Session> {
|
||||||
|
logger.debug('creating session');
|
||||||
|
|
||||||
|
if (inspector == null) {
|
||||||
|
throw new Error('the inspector module is not available for this version of node');
|
||||||
|
}
|
||||||
|
|
||||||
|
let session = null;
|
||||||
|
try {
|
||||||
|
session = new inspector.Session();
|
||||||
|
} catch (err) {
|
||||||
|
throw new Error(`error creating inspector session: ${err.message}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
session.connect();
|
||||||
|
} catch (err) {
|
||||||
|
throw new Error(`error connecting inspector session: ${err.message}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Session(logger, session);
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Session {
|
||||||
|
readonly logger: Logger;
|
||||||
|
private session: any;
|
||||||
|
|
||||||
|
constructor(logger: Logger, session: any) {
|
||||||
|
this.logger = logger;
|
||||||
|
this.session = session;
|
||||||
|
}
|
||||||
|
|
||||||
|
async destroy() {
|
||||||
|
this.session.disconnect();
|
||||||
|
this.session = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
on(event: string, handler: any) {
|
||||||
|
this.session.on(event, handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
async post(method: string, args?: any) {
|
||||||
|
this.logger.debug(`posting method ${method} ${JSON.stringify(args)}`);
|
||||||
|
if (this.session == null) {
|
||||||
|
throw new Error('session disconnected');
|
||||||
|
}
|
||||||
|
|
||||||
|
const deferred = createDeferred();
|
||||||
|
|
||||||
|
this.session.post(method, args, (err: any, response: any) => {
|
||||||
|
if (err) {
|
||||||
|
this.logger.debug(`error from method ${method}: ${err.message}`);
|
||||||
|
return deferred.reject(err);
|
||||||
|
}
|
||||||
|
deferred.resolve(response);
|
||||||
|
});
|
||||||
|
|
||||||
|
return deferred.promise;
|
||||||
|
}
|
||||||
|
}
|
29
examples/v8_profiler_examples/server/plugin.ts
Normal file
29
examples/v8_profiler_examples/server/plugin.ts
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License
|
||||||
|
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||||
|
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||||
|
* Side Public License, v 1.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Plugin, Logger, CoreSetup, PluginInitializerContext } from '@kbn/core/server';
|
||||||
|
|
||||||
|
import { registerRoutes } from './routes';
|
||||||
|
|
||||||
|
// this plugin's dependencies
|
||||||
|
export class V8ProfilerExamplesPlugin implements Plugin<void, void> {
|
||||||
|
readonly logger: Logger;
|
||||||
|
|
||||||
|
constructor(initializerContext: PluginInitializerContext) {
|
||||||
|
this.logger = initializerContext.logger.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public setup(core: CoreSetup) {
|
||||||
|
const router = core.http.createRouter();
|
||||||
|
registerRoutes(this.logger, router);
|
||||||
|
}
|
||||||
|
|
||||||
|
public start() {}
|
||||||
|
|
||||||
|
public stop() {}
|
||||||
|
}
|
84
examples/v8_profiler_examples/server/routes/common.ts
Normal file
84
examples/v8_profiler_examples/server/routes/common.ts
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License
|
||||||
|
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||||
|
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||||
|
* Side Public License, v 1.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Logger, IKibanaResponse, KibanaResponseFactory } from '@kbn/core/server';
|
||||||
|
import { createSession, Session } from '../lib/session';
|
||||||
|
import { createDeferred } from '../lib/deferred';
|
||||||
|
|
||||||
|
type StopProfilingFn = () => Promise<any>;
|
||||||
|
type StartProfilingFn<ArgType> = (session: Session, args: ArgType) => Promise<StopProfilingFn>;
|
||||||
|
|
||||||
|
export async function handleRoute<ArgType>(
|
||||||
|
startProfiling: StartProfilingFn<ArgType>,
|
||||||
|
args: ArgType,
|
||||||
|
logger: Logger,
|
||||||
|
response: KibanaResponseFactory,
|
||||||
|
duration: number,
|
||||||
|
type: string
|
||||||
|
): Promise<IKibanaResponse> {
|
||||||
|
let session: Session;
|
||||||
|
|
||||||
|
try {
|
||||||
|
session = await createSession(logger);
|
||||||
|
} catch (err) {
|
||||||
|
const message = `unable to create session: ${err.message}`;
|
||||||
|
logger.error(message);
|
||||||
|
return response.badRequest({ body: message });
|
||||||
|
}
|
||||||
|
|
||||||
|
const deferred = createDeferred();
|
||||||
|
let stopProfiling: any;
|
||||||
|
try {
|
||||||
|
stopProfiling = await startProfiling(session, args);
|
||||||
|
} catch (err) {
|
||||||
|
const message = `unable to start ${type} profiling: ${err.message}`;
|
||||||
|
logger.error(message);
|
||||||
|
return response.badRequest({ body: message });
|
||||||
|
}
|
||||||
|
|
||||||
|
setTimeout(whenDone, 1000 * duration);
|
||||||
|
|
||||||
|
let profile;
|
||||||
|
async function whenDone() {
|
||||||
|
try {
|
||||||
|
profile = await stopProfiling();
|
||||||
|
} catch (err) {
|
||||||
|
logger.warn(`unable to capture ${type} profile: ${err.message}`);
|
||||||
|
}
|
||||||
|
deferred.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
await deferred.promise;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await session.destroy();
|
||||||
|
} catch (err) {
|
||||||
|
logger.warn(`unable to destroy session: ${err.message}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (profile == null) {
|
||||||
|
const message = `unable to capture ${type} profile`;
|
||||||
|
logger.error(message);
|
||||||
|
return response.badRequest({ body: message });
|
||||||
|
}
|
||||||
|
|
||||||
|
const fileName = new Date()
|
||||||
|
.toISOString()
|
||||||
|
.replace('T', '_')
|
||||||
|
.replace(/\//g, '-')
|
||||||
|
.replace(/:/g, '-')
|
||||||
|
.substring(5, 19);
|
||||||
|
|
||||||
|
return response.ok({
|
||||||
|
body: profile,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/octet-stream',
|
||||||
|
'Content-Disposition': `attachment; filename="${fileName}.${type}profile"`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
37
examples/v8_profiler_examples/server/routes/cpu_profile.ts
Normal file
37
examples/v8_profiler_examples/server/routes/cpu_profile.ts
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License
|
||||||
|
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||||
|
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||||
|
* Side Public License, v 1.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { schema } from '@kbn/config-schema';
|
||||||
|
import { Logger, IRouter } from '@kbn/core/server';
|
||||||
|
import { startProfiling } from '../lib/cpu_profile';
|
||||||
|
import { handleRoute } from './common';
|
||||||
|
|
||||||
|
const routeValidation = {
|
||||||
|
query: schema.object({
|
||||||
|
// seconds to run the profile
|
||||||
|
duration: schema.number({ defaultValue: 5 }),
|
||||||
|
// microseconds, v8 default is 1000
|
||||||
|
interval: schema.number({ defaultValue: 1000 }),
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
const routeConfig = {
|
||||||
|
path: '/_dev/cpu_profile',
|
||||||
|
validate: routeValidation,
|
||||||
|
};
|
||||||
|
|
||||||
|
export function registerRoute(logger: Logger, router: IRouter): void {
|
||||||
|
router.get(routeConfig, async (context, request, response) => {
|
||||||
|
const { duration, interval } = request.query;
|
||||||
|
const args = {
|
||||||
|
interval,
|
||||||
|
};
|
||||||
|
|
||||||
|
return await handleRoute(startProfiling, args, logger, response, duration, 'cpu');
|
||||||
|
});
|
||||||
|
}
|
41
examples/v8_profiler_examples/server/routes/heap_profile.ts
Normal file
41
examples/v8_profiler_examples/server/routes/heap_profile.ts
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License
|
||||||
|
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||||
|
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||||
|
* Side Public License, v 1.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { schema } from '@kbn/config-schema';
|
||||||
|
import { Logger, IRouter } from '@kbn/core/server';
|
||||||
|
import { startProfiling } from '../lib/heap_profile';
|
||||||
|
import { handleRoute } from './common';
|
||||||
|
|
||||||
|
const routeValidation = {
|
||||||
|
query: schema.object({
|
||||||
|
// seconds to run the profile
|
||||||
|
duration: schema.number({ defaultValue: 5 }),
|
||||||
|
// Average sample interval in bytes. The default value is 32768 bytes.
|
||||||
|
interval: schema.number({ defaultValue: 32768 }),
|
||||||
|
includeMajorGC: schema.boolean({ defaultValue: true }),
|
||||||
|
includeMinorGC: schema.boolean({ defaultValue: true }),
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
const routeConfig = {
|
||||||
|
path: '/_dev/heap_profile',
|
||||||
|
validate: routeValidation,
|
||||||
|
};
|
||||||
|
|
||||||
|
export function registerRoute(logger: Logger, router: IRouter): void {
|
||||||
|
router.get(routeConfig, async (context, request, response) => {
|
||||||
|
const { duration, interval, includeMajorGC, includeMinorGC } = request.query;
|
||||||
|
const args = {
|
||||||
|
samplingInterval: interval,
|
||||||
|
includeObjectsCollectedByMajorGC: includeMajorGC,
|
||||||
|
includeObjectsCollectedByMinorGC: includeMinorGC,
|
||||||
|
};
|
||||||
|
|
||||||
|
return await handleRoute(startProfiling, args, logger, response, duration, 'heap');
|
||||||
|
});
|
||||||
|
}
|
17
examples/v8_profiler_examples/server/routes/index.ts
Normal file
17
examples/v8_profiler_examples/server/routes/index.ts
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License
|
||||||
|
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||||
|
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||||
|
* Side Public License, v 1.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Logger, IRouter } from '@kbn/core/server';
|
||||||
|
|
||||||
|
import { registerRoute as registerRouteCpuProfile } from './cpu_profile';
|
||||||
|
import { registerRoute as registerRouteHeapProfile } from './heap_profile';
|
||||||
|
|
||||||
|
export function registerRoutes(logger: Logger, router: IRouter): void {
|
||||||
|
registerRouteCpuProfile(logger, router);
|
||||||
|
registerRouteHeapProfile(logger, router);
|
||||||
|
}
|
18
examples/v8_profiler_examples/tsconfig.json
Normal file
18
examples/v8_profiler_examples/tsconfig.json
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
"extends": "../../tsconfig.base.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "target/types"
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"index.ts",
|
||||||
|
"server/**/*.ts",
|
||||||
|
"../../typings/**/*"
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"target/**/*",
|
||||||
|
],
|
||||||
|
"kbn_references": [
|
||||||
|
"@kbn/core",
|
||||||
|
"@kbn/config-schema",
|
||||||
|
]
|
||||||
|
}
|
|
@ -731,6 +731,7 @@
|
||||||
"@kbn/utility-types-jest": "link:packages/kbn-utility-types-jest",
|
"@kbn/utility-types-jest": "link:packages/kbn-utility-types-jest",
|
||||||
"@kbn/utils": "link:packages/kbn-utils",
|
"@kbn/utils": "link:packages/kbn-utils",
|
||||||
"@kbn/ux-plugin": "link:x-pack/plugins/ux",
|
"@kbn/ux-plugin": "link:x-pack/plugins/ux",
|
||||||
|
"@kbn/v8-profiler-examples-plugin": "link:examples/v8_profiler_examples",
|
||||||
"@kbn/vis-default-editor-plugin": "link:src/plugins/vis_default_editor",
|
"@kbn/vis-default-editor-plugin": "link:src/plugins/vis_default_editor",
|
||||||
"@kbn/vis-type-gauge-plugin": "link:src/plugins/vis_types/gauge",
|
"@kbn/vis-type-gauge-plugin": "link:src/plugins/vis_types/gauge",
|
||||||
"@kbn/vis-type-heatmap-plugin": "link:src/plugins/vis_types/heatmap",
|
"@kbn/vis-type-heatmap-plugin": "link:src/plugins/vis_types/heatmap",
|
||||||
|
|
|
@ -1478,6 +1478,8 @@
|
||||||
"@kbn/utils/*": ["packages/kbn-utils/*"],
|
"@kbn/utils/*": ["packages/kbn-utils/*"],
|
||||||
"@kbn/ux-plugin": ["x-pack/plugins/ux"],
|
"@kbn/ux-plugin": ["x-pack/plugins/ux"],
|
||||||
"@kbn/ux-plugin/*": ["x-pack/plugins/ux/*"],
|
"@kbn/ux-plugin/*": ["x-pack/plugins/ux/*"],
|
||||||
|
"@kbn/v8-profiler-examples-plugin": ["examples/v8_profiler_examples"],
|
||||||
|
"@kbn/v8-profiler-examples-plugin/*": ["examples/v8_profiler_examples/*"],
|
||||||
"@kbn/validate-next-docs-cli": ["packages/kbn-validate-next-docs-cli"],
|
"@kbn/validate-next-docs-cli": ["packages/kbn-validate-next-docs-cli"],
|
||||||
"@kbn/validate-next-docs-cli/*": ["packages/kbn-validate-next-docs-cli/*"],
|
"@kbn/validate-next-docs-cli/*": ["packages/kbn-validate-next-docs-cli/*"],
|
||||||
"@kbn/vis-default-editor-plugin": ["src/plugins/vis_default_editor"],
|
"@kbn/vis-default-editor-plugin": ["src/plugins/vis_default_editor"],
|
||||||
|
|
|
@ -5714,6 +5714,10 @@
|
||||||
version "0.0.0"
|
version "0.0.0"
|
||||||
uid ""
|
uid ""
|
||||||
|
|
||||||
|
"@kbn/v8-profiler-examples-plugin@link:examples/v8_profiler_examples":
|
||||||
|
version "0.0.0"
|
||||||
|
uid ""
|
||||||
|
|
||||||
"@kbn/validate-next-docs-cli@link:packages/kbn-validate-next-docs-cli":
|
"@kbn/validate-next-docs-cli@link:packages/kbn-validate-next-docs-cli":
|
||||||
version "0.0.0"
|
version "0.0.0"
|
||||||
uid ""
|
uid ""
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue