Extracting cpu profile and memory dump from a live Express.js app

Still in the same vibe of How I solved a memory leak in a NodeJS worker, I’m going to describe how I created an entrypoint on Express.js to extract cpu profile logs and heap dump using v8-profiler.

The first problem that I’ve found was a segfault when trying to generate the heap dumps, that I promptly noticed in the issue tracker that it is a compatibility bug related to node 8.x. Following a user’s suggestion, I replaced the v8-profile by v8-profile-node8 for now. Despite the horrible documentation and random v8-profiler is pretty handy.

Here is my code. Enjoy:

import * as profiler from "v8-profiler-node8";
if (process.env.ENABLE_PROFILE) {
  router.get("/admin/memory_profile", (_req, res) => {
    var snapshot = profiler.takeSnapshot();
    let contentDisposition = `attachment; filename="${process.pid}-${Date.now()}.heapsnapshot"`;
    res.setHeader("Content-type", "application/pdf");
    res.setHeader("Content-disposition", contentDisposition);
    snapshot
      .export()
      .pipe(res)
      .on("finish", () => {
        console.log("finished sending dump");
        snapshot.delete();
      });
  });

  // Only allow one capture per thread
  let capturingCpuProfile = false;
  router.get("/admin/cpu_profile", (_req, res) => {
    if (capturingCpuProfile) {
      return res.status(400).send("CPU profile being captured");
    }
    capturingCpuProfile = true;

    profiler.startProfiling();

    // Let it capture the cpu log for 30 seconds and then downloads the file
    setTimeout(() => {
      capturingCpuProfile = false;
      const profile = profiler.stopProfiling();

      let contentDisposition = `attachment; filename="${process.pid}-${Date.now()}.cpuprofile"`;
      res.setHeader("Content-type", "application/pdf");
      res.setHeader("Content-disposition", contentDisposition);

      profile
        .export()
        .pipe(res)
        .on("finish", function () {
          profile.delete();
        });
    }, 30000);
  });
}

I used that ENABLE_PROFILE environment variable because I don’t want to let it available on production as default. It’s a good idea to prevent unauthorized access too.

After downloading the files, just load them on chrome dev tools. For memory leak debugging, take a look at my previous post.

Caveats

  • Generating a memory dump uses a lot of memory, so make sure that the process is using less than half of the available memory.
  • The memory dump file must have the extension .heapdump
  • The cpu profile log must have the extension .cpuprofile

Memory consumption