Giter Club home page Giter Club logo

pyroscope-nodejs's Introduction

Pyroscope nodejs package

This is the nodejs profiling package for Grafana Pyroscope. It is based on the work of @datadog/pprof and v8's sample based profiler. And it adds:

  • Wall and CPU profiles with support for dynamic tags
  • Pull mode using express middleware
  • Ability to Export to Grafana Pyroscope

Downloads

Visit the npm package to find the latest version of this package.

Usage

Visit docs page for usage and configuration documentation.

Maintainers

This package is maintained by @grafana/pyroscope-nodejs. Mention this team on issues or PRs for feedback.

pyroscope-nodejs's People

Contributors

brunobastosg avatar bryanhuhta avatar cclauss avatar dependabot[bot] avatar eh-am avatar jwatzman avatar k4kratik avatar kolesnikovae avatar korniltsev avatar mightyjohnney avatar petethepig avatar rperry2174 avatar serhii-riabov avatar shaleynikov avatar simonswine avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

pyroscope-nodejs's Issues

Ship with sourcemaps

It would be nice to have sourcemaps shipped so that we can debug easily when issues occur.

Log errors to stdout/err

When first starting the

Pyroscope.init({
  serverAddress: 'https://pyroscope.cloud',
  appName: 'nodejs-test',
})

Pyroscope.start()

And error was thrown:

throw 'Pyroscope is not configured. Please call init() first.';

But I was calling init, which is confusing.

By setting the DEBUG environment variable, the error message is shown:

 DEBUG='pyroscope*' node main.js
  pyroscope Pyroscope is running on a cloud server, but no authToken was provided. Pyroscope will not be able to ingest data. +1ms

Therefore we should just log that message to stdout/err instead of hiding behind an env var.

PPROF: AttributeError: 'NoneType' object has no attribute 'groupdict'

[NodeJS 18.19.0, Mac Mini M1 (Apple Silicon) on OS X Sonoma 14.2.1 (23C71), pnpm 8.15.3]

Can't add package to repo, getting pprof errors below.

I add "@pyroscope/nodejs": "^0.2.6", or "@pyroscope/nodejs": "^0.2.9", to package.json, and then do pnpm install...

Progress: resolved 2287, reused 2246, downloaded 0, added 0, done
node_modules/.pnpm/[email protected]/node_modules/pprof: Running install script, failed in 1s
.../.pnpm/[email protected]/node_modules/pprof install$ node-pre-gyp install --fallback-to-build
│ node-pre-gyp info it worked if it ends with ok
│ node-pre-gyp info using [email protected]
│ node-pre-gyp info using [email protected] | darwin | arm64
│ node-pre-gyp info check checked for "/Users/giro/auditboard-dev-env/soxhub-api/node_modules/.pnpm/[email protected]/node_modules/pprof/build/node-v108-darwin-arm64-unknown/p…
│ node-pre-gyp http GET https://storage.googleapis.com/cloud-profiler/pprof-nodejs/release/v4.0.0/node-v108-darwin-arm64-unknown.tar.gz
│ node-pre-gyp ERR! install response status 404 Not Found on https://storage.googleapis.com/cloud-profiler/pprof-nodejs/release/v4.0.0/node-v108-darwin-arm64-unknown.tar…
│ node-pre-gyp WARN Pre-built binaries not installable for [email protected] and [email protected] (node-v108 ABI, unknown) (falling back to source compile with node-gyp)
│ node-pre-gyp WARN Hit error response status 404 Not Found on https://storage.googleapis.com/cloud-profiler/pprof-nodejs/release/v4.0.0/node-v108-darwin-arm64-unknown.t…
│ gyp info it worked if it ends with ok
│ gyp info using [email protected]
│ gyp info using [email protected] | darwin | arm64
│ gyp info ok
│ gyp info it worked if it ends with ok
│ gyp info using [email protected]
│ gyp info using [email protected] | darwin | arm64
│ gyp info find Python using Python version 3.11.4 found at "/Users/giro/miniconda3/bin/python3"
│ gyp info spawn /Users/giro/miniconda3/bin/python3
│ gyp info spawn args [
│ gyp info spawn args '/Users/giro/Library/pnpm/global/5/.pnpm/[email protected]/node_modules/pnpm/dist/node_modules/node-gyp/gyp/gyp_main.py',
│ gyp info spawn args 'binding.gyp',
│ gyp info spawn args '-f',
│ gyp info spawn args 'make',
│ gyp info spawn args '-I',
│ gyp info spawn args '/Users/giro/auditboard-dev-env/soxhub-api/node_modules/.pnpm/[email protected]/node_modules/pprof/build/config.gypi',
│ gyp info spawn args '-I',
│ gyp info spawn args '/Users/giro/Library/pnpm/global/5/.pnpm/[email protected]/node_modules/pnpm/dist/node_modules/node-gyp/addon.gypi',
│ gyp info spawn args '-I',
│ gyp info spawn args '/Users/giro/Library/Caches/node-gyp/18.19.0/include/node/common.gypi',
│ gyp info spawn args '-Dlibrary=shared_library',
│ gyp info spawn args '-Dvisibility=default',
│ gyp info spawn args '-Dnode_root_dir=/Users/giro/Library/Caches/node-gyp/18.19.0',
│ gyp info spawn args '-Dnode_gyp_dir=/Users/giro/Library/pnpm/global/5/.pnpm/[email protected]/node_modules/pnpm/dist/node_modules/node-gyp',
│ gyp info spawn args '-Dnode_lib_file=/Users/giro/Library/Caches/node-gyp/18.19.0/<(target_arch)/node.lib',
│ gyp info spawn args '-Dmodule_root_dir=/Users/giro/auditboard-dev-env/soxhub-api/node_modules/.pnpm/[email protected]/node_modules/pprof',
│ gyp info spawn args '-Dnode_engine=v8',
│ gyp info spawn args '--depth=.',
│ gyp info spawn args '--no-parallel',
│ gyp info spawn args '--generator-output',
│ gyp info spawn args 'build',
│ gyp info spawn args '-Goutput_dir=.'
│ gyp info spawn args ]
│ No receipt for 'com.apple.pkg.CLTools_Executables' found at '/'.
│ No receipt for 'com.apple.pkg.DeveloperToolsCLILeo' found at '/'.
│ No receipt for 'com.apple.pkg.DeveloperToolsCLI' found at '/'.
│ Traceback (most recent call last):
│ File "/Users/giro/Library/pnpm/global/5/.pnpm/[email protected]/node_modules/pnpm/dist/node_modules/node-gyp/gyp/pylib/gyp/xcode_emulation.py", line 1502, in XcodeVersion
│ version_list = GetStdoutQuiet(["xcodebuild", "-version"]).splitlines()
│ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
│ File "/Users/giro/Library/pnpm/global/5/.pnpm/[email protected]/node_modules/pnpm/dist/node_modules/node-gyp/gyp/pylib/gyp/xcode_emulation.py", line 1563, in GetStdoutQuiet
│ raise GypError("Error %d running %s" % (job.returncode, cmdlist[0]))
│ gyp.common.GypError: Error 1 running xcodebuild
│ During handling of the above exception, another exception occurred:
│ Traceback (most recent call last):
│ File "/Users/giro/Library/pnpm/global/5/.pnpm/[email protected]/node_modules/pnpm/dist/node_modules/node-gyp/gyp/gyp_main.py", line 45, in
│ sys.exit(gyp.script_main())
│ ^^^^^^^^^^^^^^^^^
│ File "/Users/giro/Library/pnpm/global/5/.pnpm/[email protected]/node_modules/pnpm/dist/node_modules/node-gyp/gyp/pylib/gyp/init.py", line 686, in script_main
│ return main(sys.argv[1:])
│ ^^^^^^^^^^^^^^^^^^
│ File "/Users/giro/Library/pnpm/global/5/.pnpm/[email protected]/node_modules/pnpm/dist/node_modules/node-gyp/gyp/pylib/gyp/init.py", line 678, in main
│ return gyp_main(args)
│ ^^^^^^^^^^^^^^
│ File "/Users/giro/Library/pnpm/global/5/.pnpm/[email protected]/node_modules/pnpm/dist/node_modules/node-gyp/gyp/pylib/gyp/init.py", line 663, in gyp_main
│ generator.GenerateOutput(flat_list, targets, data, params)
│ File "/Users/giro/Library/pnpm/global/5/.pnpm/[email protected]/node_modules/pnpm/dist/node_modules/node-gyp/gyp/pylib/gyp/generator/make.py", line 2651, in GenerateOutput
│ writer.Write(
│ File "/Users/giro/Library/pnpm/global/5/.pnpm/[email protected]/node_modules/pnpm/dist/node_modules/node-gyp/gyp/pylib/gyp/generator/make.py", line 902, in Write
│ self.WriteCopies(spec["copies"], extra_outputs, part_of_all)
│ File "/Users/giro/Library/pnpm/global/5/.pnpm/[email protected]/node_modules/pnpm/dist/node_modules/node-gyp/gyp/pylib/gyp/generator/make.py", line 1298, in WriteCopies
│ env = self.GetSortedXcodeEnv()
│ ^^^^^^^^^^^^^^^^^^^^^^^^
│ File "/Users/giro/Library/pnpm/global/5/.pnpm/[email protected]/node_modules/pnpm/dist/node_modules/node-gyp/gyp/pylib/gyp/generator/make.py", line 2269, in GetSortedXcode…
│ return gyp.xcode_emulation.GetSortedXcodeEnv(
│ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
│ File "/Users/giro/Library/pnpm/global/5/.pnpm/[email protected]/node_modules/pnpm/dist/node_modules/node-gyp/gyp/pylib/gyp/xcode_emulation.py", line 1888, in GetSortedXcod…
│ env = _GetXcodeEnv(
│ ^^^^^^^^^^^^^
│ File "/Users/giro/Library/pnpm/global/5/.pnpm/[email protected]/node_modules/pnpm/dist/node_modules/node-gyp/gyp/pylib/gyp/xcode_emulation.py", line 1743, in _GetXcodeEnv
│ "XCODE_VERSION_ACTUAL": XcodeVersion()[0],
│ ^^^^^^^^^^^^^^
│ File "/Users/giro/Library/pnpm/global/5/.pnpm/[email protected]/node_modules/pnpm/dist/node_modules/node-gyp/gyp/pylib/gyp/xcode_emulation.py", line 1513, in XcodeVersion
│ version = CLTVersion() # macOS Catalina returns 11.0.0.0.1.1567737322
│ ^^^^^^^^^^^^
│ File "/Users/giro/Library/pnpm/global/5/.pnpm/[email protected]/node_modules/pnpm/dist/node_modules/node-gyp/gyp/pylib/gyp/xcode_emulation.py", line 1551, in CLTVersion
│ return re.search(regex, output).groupdict()["version"]
│ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
│ AttributeError: 'NoneType' object has no attribute 'groupdict'
│ gyp ERR! configure error
│ gyp ERR! stack Error: gyp failed with exit code: 1
│ gyp ERR! stack at ChildProcess.onCpExit (/Users/giro/Library/pnpm/global/5/.pnpm/[email protected]/node_modules/pnpm/dist/node_modules/node-gyp/lib/configure.js:325:16)
│ gyp ERR! stack at ChildProcess.emit (node:events:517:28)
│ gyp ERR! stack at ChildProcess._handle.onexit (node:internal/child_process:292:12)
│ gyp ERR! System Darwin 23.2.0
│ gyp ERR! command "/Users/giro/Library/pnpm/nodejs/18.19.0/bin/node" "/Users/giro/Library/pnpm/global/5/.pnpm/[email protected]/node_modules/pnpm/dist/node_modules/node-gyp/b…
│ gyp ERR! cwd /Users/giro/auditboard-dev-env/soxhub-api/node_modules/.pnpm/[email protected]/node_modules/pprof
│ gyp ERR! node -v v18.19.0
│ gyp ERR! node-gyp -v v9.4.1
│ gyp ERR! not ok
│ node-pre-gyp ERR! build error
│ node-pre-gyp ERR! stack Error: Failed to execute '/Users/giro/Library/pnpm/nodejs/18.19.0/bin/node /Users/giro/Library/pnpm/global/5/.pnpm/[email protected]/node_modules/pnp…
│ node-pre-gyp ERR! stack at ChildProcess. (/Users/giro/auditboard-dev-env/soxhub-api/node_modules/.pnpm/@mapbox[email protected]/node_modules/@mapbox/…
│ node-pre-gyp ERR! stack at ChildProcess.emit (node:events:517:28)
│ node-pre-gyp ERR! stack at maybeClose (node:internal/child_process:1098:16)
│ node-pre-gyp ERR! stack at ChildProcess._handle.onexit (node:internal/child_process:303:5)
│ node-pre-gyp ERR! System Darwin 23.2.0
│ node-pre-gyp ERR! command "/Users/giro/Library/pnpm/nodejs/18.19.0/bin/node" "/Users/giro/auditboard-dev-env/soxhub-api/node_modules/.pnpm/@mapbox[email protected]/…
│ node-pre-gyp ERR! cwd /Users/giro/auditboard-dev-env/soxhub-api/node_modules/.pnpm/[email protected]/node_modules/pprof
│ node-pre-gyp ERR! node -v v18.19.0
│ node-pre-gyp ERR! node-pre-gyp -v v1.0.11
│ node-pre-gyp ERR! not ok
│ Failed to execute '/Users/giro/Library/pnpm/nodejs/18.19.0/bin/node /Users/giro/Library/pnpm/global/5/.pnpm/[email protected]/node_modules/pnpm/dist/node_modules/node-gyp/bi…
└─ Failed in 1s at /Users/giro/auditboard-dev-env/soxhub-api/node_modules/.pnpm/[email protected]/node_modules/pprof
 ELIFECYCLE  Command failed with exit code 1.

Add profile processing option to Express middleware for improved function naming

Hi,
Currently, the ContinuousProfiler includes profile processing functionality. We propose extending this capability to the Express middleware by introducing a configuration option that utilizes the processProfile utility function.

Key points:

  1. New Configuration Option:

    • Introduce a config option in the Express middleware to enable profile processing.
    • This would leverage the existing processProfile utility function.
  2. Documentation Update:

    • The current documentation for pull mode with Express middleware is incomplete.
    • The NodeJS SDK documentation only mentions pull mode with Grafana Alloy or Grafana Agent.
    • We should add detailed instructions for using pull mode with Express middleware.
  3. Improved Function Naming:

    • This feature would be particularly beneficial for scenarios involving anonymous functions.
    • Instead of generic "anonymous function" labels in the profile output, users would see filenames, enhancing debuggability.
    • This improvement is inspired by the discussion in issue #84.
  4. Real-world Use Case:

    • Our team encountered this need and believes it could be generally useful.

    • As a temporary solution, we patched the middleware to call the processProfile utility function:

      async function collectProfile(profiler) {
          const profile = profiler.profile().profile;
          const processedProfile = processProfile(profile)
          profiler.stop();
          return encode(processedProfile);
      }
    • This workaround functions correctly with pull mode.

By implementing this feature, we can enhance the profiling capabilities of the Express middleware, providing more detailed and useful information to users, especially in scenarios involving anonymous functions.

I am more than happy to implement this feature and contribute to the project if needed. Thanks

make `.stop()` flush

This is going to be tricky.

When stopping the profiler, it only sets up a stop flag
https://github.com/pyroscope-io/pyroscope-nodejs/blob/09d3f4d1b77d8640181e871db0fa3d60919e4a7d/src/index.ts#L284
Which only affects the next profiling round
https://github.com/pyroscope-io/pyroscope-nodejs/blob/09d3f4d1b77d8640181e871db0fa3d60919e4a7d/src/index.ts#L257-L264

That means that it may take up to 10 seconds (the profiling round) to exit
https://github.com/pyroscope-io/pyroscope-nodejs/blob/09d3f4d1b77d8640181e871db0fa3d60919e4a7d/src/index.ts#L204

This behaviour is pretty clear when running tests, where the test runner hangs up to 10 seconds, and the last profile captured is full of idle, since the program it's not doing anything.

replace path when running using workspaces

https://docs.npmjs.com/cli/v7/using-npm/workspaces
When running using workspaces, the cwd (aka current directory from what yarn/npm is running from) doesn't necessarily match what's in the stack trace.

Therefore the following piece of code doesn't replace the current path with . correctly:
https://github.com/pyroscope-io/pyroscope-nodejs/blob/09d3f4d1b77d8640181e871db0fa3d60919e4a7d/src/index.ts#L123-L127

Here's an example flamegraph https://flamegraph.com/share/00f3cbd1-a17d-11ed-87c6-42e8e9dddd83
And an example repository https://github.com/eh-am/example-pyroscope-nodejs-workspace

Typescript types are not properly emitted

Description

I cannot easily access types from the package in order to use it in my ts codebase.

Reproduction of the issue

  1. Install any NodeJS >= 16.
  2. Given a root directory, initialize a new npm package: npm init
  3. Install typescript@5 and @pyroscope/[email protected] dependencies.
  4. Add a tsconfig. An example might be:
{
  "$schema": "http://json.schemastore.org/tsconfig",
  "compilerOptions": {
    "declaration": true,
    "declarationMap": true,
    "experimentalDecorators": true,
    "esModuleInterop": true,
    "exactOptionalPropertyTypes": true,
    "forceConsistentCasingInFileNames": true,
    "lib": ["ES2022"],
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "noFallthroughCasesInSwitch": true,
    "noImplicitOverride": true,
    "noImplicitReturns": true,
    "noPropertyAccessFromIndexSignature": true,
    "noUncheckedIndexedAccess": true,
    "resolveJsonModule": true,
    "skipLibCheck": true,
    "sourceMap": true,
    "strict": true,
    "target": "ES2022",
    "outDir": "./dist",
    "rootDir": "./src"
  },
  "include": ["src"]
}
  1. Add a src/index.ts source file:
import Pyroscope from '@pyroscope/nodejs';

Pyroscope.init({
  appName: 'nodejs',
  serverAddress: 'http://localhost:4040',
  sourceMapPath: ['.'],
});
Pyroscope.startHeapProfiling();
Pyroscope.startCpuProfiling();
  1. Compile source files.

Expected behavior

  • No compiler error should be thrown

Actual behavior:

  • A compiler error is thrown:
error TS7016: Could not find a declaration file for module '@pyroscope/nodejs'. '/home/bob/[...]/node_modules/@pyroscope/nodejs/dist/cjs/index.js' implicitly has an 'any' type.

Additional context.

  • Types should not be exposed in a types folder in the @pyroscope/nodejs package. The build:cjs and build:esm npm scripts should emit declaration files, you can achieve this with the "declaration": true, option enabled in their associated tsconfig files. build:types is no longer necessary, it adds unreached types to the package.

Consider this tool when solving this issue, it allows you to upload the artifact generated by the npm pack script in order to analyze it.

Improve test coverage for the SDK

With the recent uptick of feature and maintenance work that this SDK has seen, we've introduced a few bugs.

These may have been able to be mitigated with more thorough test coverage. We should take a stab at improving the test coverage to help us be confident new changes don't break anything (or tell us when we are making a breaking change).

`PYROSCOPE_SAMPLING_DURATION` is never used

The env var is assigned here
https://github.com/pyroscope-io/pyroscope-nodejs/blob/09d3f4d1b77d8640181e871db0fa3d60919e4a7d/src/index.ts#L30

And then it's used here
https://github.com/pyroscope-io/pyroscope-nodejs/blob/09d3f4d1b77d8640181e871db0fa3d60919e4a7d/src/index.ts#L204

But the expression seems wrong

(seconds || 10) * 1000 || Number(SAMPLING_DURATION_MS)

Since (seconds || 10) will default to 10, which is then multiplied by 1000, which is truthy. Therefore it never defaults to whatever SAMPLING_DURATION_MS is.

Contexts are not supported

Hi,
When starting pyroscope on my nodejs app (using expressjs) I'm getting an error: Contexts are not supported:

uncaughtException: Contexts are not supported.
TypeError: Contexts are not supported.
    at Object.start (C:\Users\alovergne\Documents\GithubPerso\Jaily\src\back\node_modules\@datadog\pprof\out\src\time-profiler.js:61:17)
    at WallProfiler.start (C:\Users\alovergne\Documents\GithubPerso\Jaily\src\back\node_modules\@pyroscope\nodejs\dist\cjs\profilers\wall-profiler.js:47:26)
    at ContinuousProfiler.start (C:\Users\alovergne\Documents\GithubPerso\Jaily\src\back\node_modules\@pyroscope\nodejs\dist\cjs\profilers\continuous-profiler.js:28:23)
    at startWallProfiling (C:\Users\alovergne\Documents\GithubPerso\Jaily\src\back\node_modules\@pyroscope\nodejs\dist\cjs\index.js:42:58)
    at Object.start (C:\Users\alovergne\Documents\GithubPerso\Jaily\src\back\node_modules\@pyroscope\nodejs\dist\cjs\index.js:62:5)
    at Object.<anonymous> (C:\Users\alovergne\Documents\GithubPerso\Jaily\src\back\src\index.js:78:11)
    at Module._compile (node:internal/modules/cjs/loader:1358:14)
    at Module._extensions..js (node:internal/modules/cjs/loader:1416:10)
    at Module.load (node:internal/modules/cjs/loader:1208:32)
    at Module._load (node:internal/modules/cjs/loader:1024:12) {"date":"Wed May 29 2024 16:13:37 GMT+0200 (heure d’été d’Europe centrale)","error":{},"exception":true,"os":{"loadavg":[0,0,0],"uptime":25848.796},"process":{"argv":["C:\\Program Files\\nodejs\\node.exe","C:\\Users\\alovergne\\Documents\\GithubPerso\\Jaily\\src\\back\\src\\index.js"],"cwd":"C:\\Users\\alovergne\\Documents\\GithubPerso\\Jaily\\src\\back","execPath":"C:\\Program Files\\nodejs\\node.exe","gid":null,"memoryUsage":{"arrayBuffers":18264489,"external":20623267,"heapTotal":58728448,"heapUsed":38249744,"rss":81870848},"pid":23904,"uid":null,"version":"v20.14.0"},"stack":"TypeError: Contexts are not supported.\n    at Object.start (C:\\Users\\alovergne\\Documents\\GithubPerso\\Jaily\\src\\back\\node_modules\\@datadog\\pprof\\out\\src\\time-profiler.js:61:17)\n    at WallProfiler.start (C:\\Users\\alovergne\\Documents\\GithubPerso\\Jaily\\src\\back\\node_modules\\@pyroscope\\nodejs\\dist\\cjs\\profilers\\wall-profiler.js:47:26)\n    at ContinuousProfiler.start (C:\\Users\\alovergne\\Documents\\GithubPerso\\Jaily\\src\\back\\node_modules\\@pyroscope\\nodejs\\dist\\cjs\\profilers\\continuous-profiler.js:28:23)\n    at startWallProfiling (C:\\Users\\alovergne\\Documents\\GithubPerso\\Jaily\\src\\back\\node_modules\\@pyroscope\\nodejs\\dist\\cjs\\index.js:42:58)\n    at Object.start (C:\\Users\\alovergne\\Documents\\GithubPerso\\Jaily\\src\\back\\node_modules\\@pyroscope\\nodejs\\dist\\cjs\\index.js:62:5)\n    at Object.<anonymous> (C:\\Users\\alovergne\\Documents\\GithubPerso\\Jaily\\src\\back\\src\\index.js:78:11)\n    at Module._compile (node:internal/modules/cjs/loader:1358:14)\n    at Module._extensions..js (node:internal/modules/cjs/loader:1416:10)\n    at Module.load (node:internal/modules/cjs/loader:1208:32)\n    at Module._load (node:internal/modules/cjs/loader:1024:12)","trace":[{"column":17,"file":"C:\\Users\\alovergne\\Documents\\GithubPerso\\Jaily\\src\\back\\node_modules\\@datadog\\pprof\\out\\src\\time-profiler.js","function":"Object.start","line":61,"method":"start","native":false},{"column":26,"file":"C:\\Users\\alovergne\\Documents\\GithubPerso\\Jaily\\src\\back\\node_modules\\@pyroscope\\nodejs\\dist\\cjs\\profilers\\wall-profiler.js","function":"WallProfiler.start","line":47,"method":"start","native":false},{"column":23,"file":"C:\\Users\\alovergne\\Documents\\GithubPerso\\Jaily\\src\\back\\node_modules\\@pyroscope\\nodejs\\dist\\cjs\\profilers\\continuous-profiler.js","function":"ContinuousProfiler.start","line":28,"method":"start","native":false},{"column":58,"file":"C:\\Users\\alovergne\\Documents\\GithubPerso\\Jaily\\src\\back\\node_modules\\@pyroscope\\nodejs\\dist\\cjs\\index.js","function":"startWallProfiling","line":42,"method":null,"native":false},{"column":5,"file":"C:\\Users\\alovergne\\Documents\\GithubPerso\\Jaily\\src\\back\\node_modules\\@pyroscope\\nodejs\\dist\\cjs\\index.js","function":"Object.start","line":62,"method":"start","native":false},{"column":11,"file":"C:\\Users\\alovergne\\Documents\\GithubPerso\\Jaily\\src\\back\\src\\index.js","function":null,"line":78,"method":null,"native":false},{"column":14,"file":"node:internal/modules/cjs/loader","function":"Module._compile","line":1358,"method":"_compile","native":false},{"column":10,"file":"node:internal/modules/cjs/loader","function":"Module._extensions..js","line":1416,"method":".js","native":false},{"column":32,"file":"node:internal/modules/cjs/loader","function":"Module.load","line":1208,"method":"load","native":false},{"column":12,"file":"node:internal/modules/cjs/loader","function":"Module._load","line":1024,"method":"_load","native":false}]}

I tried many things like upgrading and downgrading my nodejs to different versions, but always the same problem.
Tried letting only the piece of code from documentation which is this one, same error:

const Pyroscope = require('@pyroscope/nodejs');
Pyroscope.init({
  serverAddress: '',
  appName: '',
  basicAuthUser: '',
  basicAuthPassword: ''
});
Pyroscope.start();

Does someone have an idea ?

Update axios version to a minimum of v1.6.4

Hi,
Current version of axios used have some vulnerabilities reported by snyk. Would request you to update the version to a minimum of 1.6.4 to address them.
Thank you. Will be waiting for your response.

Support Pull mode

From nodejs side we need

  1. Export basic profile collection data (cpu, heap), for people that want to add
  2. Maybe export a middleware for popular frameworks? Not entire sure how much work would be to support most popular frameworks, but worst case scenario people can implement themselves (using 1)
  3. Maybe follow go's pprof (https://pkg.go.dev/net/http/pprof) example and create a server ourselves? We could use a simple http server https://nodejs.org/en/knowledge/HTTP/servers/how-to-create-a-HTTP-server/

NodeJS cannot find module regenerator-runtime/runtime

We are attempting to add Pyroscope to one of our NodeJS framework modules. Pyroscope fails to load with the following error:

Error: Cannot find module '/Users/bhunt/Development/*****/modules/microservice/node_modules/regenerator-runtime/runtime' imported from /Users/bhunt/Development/*****/modules/microservice/node_modules/@pyroscope/nodejs/dist/esm/index.js
    at finalizeResolution (/Users/bhunt/Development/*****/modules/microservice/node_modules/ts-node/dist-raw/node-internal-modules-esm-resolve.js:366:11)
    at moduleResolve (/Users/bhunt/Development/*****/modules/microservice/node_modules/ts-node/dist-raw/node-internal-modules-esm-resolve.js:801:10)
    at Object.defaultResolve (/Users/bhunt/Development/*****/modules/microservice/node_modules/ts-node/dist-raw/node-internal-modules-esm-resolve.js:912:11)
    at /Users/bhunt/Development/*****/modules/microservice/node_modules/ts-node/src/esm.ts:218:35
    at entrypointFallback (/Users/bhunt/Development/*****/modules/microservice/node_modules/ts-node/src/esm.ts:168:34)
    at /Users/bhunt/Development/*****/modules/microservice/node_modules/ts-node/src/esm.ts:217:14
    at addShortCircuitFlag (/Users/bhunt/Development/*****/modules/microservice/node_modules/ts-node/src/esm.ts:409:21)
    at resolve (/Users/bhunt/Development/*****/modules/microservice/node_modules/ts-node/src/esm.ts:197:12)
    at nextResolve (node:internal/modules/esm/loader:163:28)
    at ESMLoader.resolve (node:internal/modules/esm/loader:841:30)
    at ESMLoader.getModuleJob (node:internal/modules/esm/loader:424:18)
    at ModuleWrap.<anonymous> (node:internal/modules/esm/module_job:76:40)
    at link (node:internal/modules/esm/module_job:75:36)

We are running NodeJS v16.19.1 with the following mocha configuration:

{
  "mocha": {
    "reporter": "mocha-multi-reporters",
    "reporter-options": [
      "configFile=mocha-reporter-config.json"
    ],
    "loader": "ts-node/esm"
  }
}

Hacking the module and changing the import to:

import 'regenerator-runtime/runtime.js';

seems to fix the problem.

Express middleware starts heap profiling but does not stop it

if, for instance, in a testsuite, app() entry point is called once for each testcase, a new express is provisioned (correctly), and middleware/handlers attempt to register each time.

but the pyroscope middleware is not idempotent -- if heap profiling is already on, it'll attempt to enable it again and crash. better to either register a shutdown hook, or to not attempt to enable heap profiling a second time if called twice in same nodejs runtime.

Memory leak when Pyroscope is enabled

When we enable Pyroscope on our application, we observe a steady increase in memory consumption. I've annotated a AWS CloudWatch graph of Node RSS memory:

Untitled drawing

Some commentary about what we saw:

  • After enabling Pyroscope, we saw memory usage increase pretty much linearly.
  • After a while, the EC2 instance as a whole became completely unresponsive. At that point I couldn't even SSH in to the instance to poke around, so I'm unable to even guess why that might be. Rebooting the entire VM "fixed" things (until they broke again).
    • You can identify the "unresponsive" periods as the ones that are missing data.
  • This was on our staging environment, so the app wasn't serving any real requests and was under basically zero load.
  • After disabling Pyroscope and making no other changes, we stopped leaking memory.
  • The graph lines that spike past 3.0GB/1.5GB are for an instance with a single copy of the app running on it. The other 4 lines are 4 copies of an app running on the same instance. I'm unsure why the two types of hosts behave differently. My best guess is that the OS starts doing a ton of swapping, or runs out of memory entirely (we're on an EC2 t3.medium instance that has 4GB of RAM, so 4 processes x 1GB each would definitely exhaust the available RAM).
  • Pyroscope itself doesn't reflect that the Node process is using anywhere close to 1GB of memory (per inuse_space, it maxed out at ~350MB of mem).

These lines of code are the only difference between "leaking memory" and "not leaking memory":

          const Pyroscope = require('@pyroscope/nodejs');
          Pyroscope.init({
            appName: 'prairielearn',
            // Assume `config` contains sensible values.
            serverAddress: config.pyroscopeServerAddress,
            authToken: config.pyroscopeAuthToken,
            tags: {
              instanceId: config.instanceId,
              ...(config.pyroscopeTags ?? {}),
            },
          });
          Pyroscope.start();

I recognize this isn't a ton of information to go off, so I'd be happy to provide anything else that might help get to the bottom of this. We'd love to use Pyroscope, but our experience so far is an obvious dealbreaker.

Use @datadog/pprof instead of the old, neglected pprof

https://www.npmjs.com/package/@datadog/pprof (1,788,492 weekly downloads) is used a lot more than https://www.npmjs.com/package/pprof (142,535 weekly downloads).

It's also difficult to use the old pprof, because there are no binaries available compatible with newer versions of Node. Therefore, installing it will use node-gyp to build pprof, which requires python and make to be installed. @datadog/pprof really easily (within seconds).

Is it possible to switch to consider if @datadog/pprof is better, and switch to it?

Add support for Windows

Currently, we don't support Windows because of our usage of dynamic labels. The DataDog profiler doesn't support dynamic labels on Windows:

https://github.com/DataDog/pprof-nodejs/blob/e7d3e053d9816e64710533738ff827c0301b82fe/bindings/profilers/wall.cc#L33

The DD_WALL_USE_SIGPROF enables dynamic labels and if it is disabled, it throws an error here:

https://github.com/DataDog/pprof-nodejs/blob/e7d3e053d9816e64710533738ff827c0301b82fe/bindings/profilers/wall.cc#L609-L611

We always enable withContexts in or SDK:

We might be able to support Windows by disabling this configuration when we detect a Windows host, or by providing a way for users to turn it off. At the very least, we should provide a gentler failure case which is more helpful for users.

See #85 for an example of the SDK not working on Windows.

Make upload interval configurable

In certain cases, e.g to reduce pressure on server / data volume, an ability to increase uploading interval is very desirable. Currently, it's hardcoded to 10 seconds.

What does `:(idle):0` represent in CPU profiles?

I'm profiling a node application, and most of the CPU time is spent in this state. The process uses ~60% of one CPU core according to system metrics (this is in Kubernetes). I'm wondering what it means exactly?

image

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.