CPUpro

repository·main·Indexed 21 days ago

https://github.com/discoveryjs/cpupro

A tool for analyzing and processing CPU profiles and logs from V8 runtimes, including Node.js, Deno, and Chromium. It features a web-based viewer, a CLI for generating HTML reports, and a programmatic Node.js API for profiling code blocks. Supported formats include V8 logs, V8 CPU profiles, Chromium Performance Profiles, and Edge Enhanced Performance Traces. Version 0.7.0.

Tokens
5.2K
Snippets
12
Records
17
Agent score
72%

What's inside cpupro

  1. Supported CPU profile formats

    main

    CPUpro processes profiles and logs collected in V8 runtimes (Node.js, Deno, and Chromium). While file extensions can be arbitrary, the format is determined by the content. Files may be compressed using gzip or deflate.

    Supported formats include:

    • V8 log (.log)
    • V8 log preprocessed (.json, using --preprocess)
    • V8 CPU profile (.cpuprofile)
    • Chromium Performance Profile (.json)
    • Edge Enhanced Performance Traces (.devtools)
  2. Use CPUpro as a Node.js preload module

    main

    You can use CPUpro as a preload module to profile an entire Node.js script without modifying its source code.

    Common Scenarios

    • Record profile, generate report, and open in browser:

      node --require cpupro path/to/script.js
    • Record profile, generate report, and write to a file:

      node --require cpupro/file path/to/script.js
      # or
      node --require cpupro/file/report path/to/script.js
    • Record profile and write to a .cpuprofile file:

      node --require cpupro/file/data path/to/script.js
    node --require cpupro path/to/script.js
  3. Use the CPUpro CLI

    main

    The CLI allows you to generate a report (a viewer with embedded data) from a profile file. You can install it globally via npm install -g cpupro or run it directly using npx cpupro.

    Commands

    • Open viewer without embedded data (opens default browser):

      cpupro
    • Open viewer with data embedded from a file:

      cpupro test.cpuprofile
    • Open viewer with data embedded from stdin:

      cat test.cpuprofile | cpupro -

    CLI Options

    FlagDescription
    -f, --filename <filename>Specify a filename for a report; should end with .htm or .html, otherwise .html will be added
    -h, --helpOutput usage information
    -n, --no-openPrevent opening a report in the browser; the report will be written to a file
    -o, --output-dir <path>Specify an output path for a report (current working directory by default)
    -v, --versionOutput version
    cpupro test.cpuprofile
  4. Use CPUpro as a Node.js library

    main

    You can integrate CPUpro directly into your Node.js application to profile specific code blocks and save the results. The API is inspired by console.profile() and console.profileEnd().

    Basic Usage (Named Profiles)

    Use profiler.profile('name') to start and profiler.profileEnd('name') to stop. The returned object allows you to save the data or generate a report.

    const profiler = require('cpupro');
    
    profiler.profile('profileName');
    
    // ... do something
    
    const profile = profiler.profileEnd('profileName');
    
    // Write data to .cpuprofile file
    profile.writeToFile('./path/to/demo.cpuprofile');
    
    // Or write a report (the viewer with embedded data) to file
    profile.report.writeToFile('report.html');
    
    // Or open the report in a browser
    profile.report.open();

    Using Profile References

    You can use the object returned by profile() directly to avoid managing names.

    const profiler = require('cpupro');
    
    const profile = profiler.profile();
    
    // ... do something
    
    // End profiling and open a report in a browser
    profile.profileEnd().openReport();

    Declarative Actions

    You can chain actions at the start of profiling. These actions will be executed automatically when profileEnd() is called or when the process exits if profileEnd() is not explicitly invoked.

    const profiler = require('cpupro');
    
    profiler.profile()
      .writeToFile('./path/to/demo.cpuprofile');
    
    // No need to call profileEnd() if you want the profile dumped on process exit
    const profiler = require('cpupro');
    
    profiler.profile('profileName');
    
    // ... do something
    
    const profile = profiler.profileEnd('profileName');
    
    profile.writeToFile('./path/to/demo.cpuprofile');
    profile.report.writeToFile('report.html');
    profile.report.open();
  5. Use createReport to generate CPU profile reports

    main

    The createReport function is the primary way to programmatically create a report from a CPU profile. It is exported from the main package entrypoint.

    const { createReport } = require('cpupro');
    
    // Usage depends on the implementation of createReport
    const report = createReport(profileData);
  6. Generate HTML reports from profile data with createReport()

    main

    The createReport function is used to transform profile data into a standalone HTML report. It supports two primary data formats:

    1. String or ArrayBufferView: If the input data is a string or an ArrayBufferView, it is treated as raw text and encoded using a raw text printer.
    2. JSON-like Objects: If the input is a standard object, it is encoded using a compressed Base64 printer.

    The function returns an object containing methods to save the report to disk or open it in a browser.

    Return Object API

    • writeToFile(filepath): Synchronously writes the HTML report to the specified filepath. If filepath is omitted, it uses a default filename.
    • writeToFileAsync(filepath): (Note: Currently behaves synchronously) Writes the report to the specified filepath.
    • open(): Attempts to open the generated report in the default system browser. If no report has been written yet, it generates a temporary report in the OS temp directory and opens it.
    const createReport = require('cpupro/lib/report');
    
    const profileData = { /* your CPU profile data */ };
    const reporter = createReport(profileData);
    
    // Option 1: Write to a specific file
    const filePath = reporter.writeToFile('my-report.html');
    
    // Option 2: Generate and open in browser immediately
    reporter.open();
  7. Use the FlameChart class to visualize profile data

    main

    The FlameChart<T> class is a high-performance visualizer for CPU profile data represented as a CallTree<T>. It renders a hierarchical flame chart using DOM elements and CSS variables for efficient updates.

    Key Workflow

    1. Instantiate: Create a new FlameChart instance.
    2. Set Data: Use setData(tree, options) to load your CallTree.
    3. Mount: Append the chart.el (an HTMLElement) to your application's DOM.
    4. Interact: The chart supports zooming (click a frame) and selecting (Meta/Cmd + click a frame).

    Customizing Data Mapping

    You can provide a SetDataOptions object to setData to control how the raw data in your CallTree is mapped to the visual representation:

    • name: Function to extract the display name from the frame data.
    • value: Function to extract the numerical value (e.g., duration) for sizing.
    • offset: Function to determine the horizontal offset.
    • children: Function to retrieve child nodes.
    • childrenSort: Determines how siblings are ordered. Options: true (sort by value descending), 'name' (sort alphabetically), a custom comparator function, or undefined.

    Events

    The FlameChart extends EventEmitter and emits several useful events:

    • render: Fired when the chart re-renders. Provides the root element, root frame, and total root value.
    • select: Fired when a node is selected. Returns (nodeIndex, prevNodeIndex).
    • zoom: Fired when the view zooms. Returns (nodeIndex, start, end) where start and end are normalized coordinates (0 to 1).
    • frame:click: Fired on clicking a frame. Returns (nodeIndex, element, event).
    • frame:enter / frame:leave: Fired when the pointer enters or leaves a frame element.
    • destroy: Fired when the chart is destroyed.
    import { FlameChart } from 'cpupro/app/views/flamechart';
    
    const chart = new FlameChart<MyDataType>();
    
    // Configure how data is read from your CallTree
    chart.setData(myCallTree, {
        name: (data) => data.functionName,
        value: (data) => data.duration,
        childrenSort: 'value' // Sort children by value descending
    });
    
    // Add to your DOM
    document.body.appendChild(chart.el);
    
    // Listen for interactions
    chart.on('zoom', (nodeIndex, start, end) => {
        console.log(`Zoomed into node ${nodeIndex} at range [${start}, ${end}]`);
    });
  8. Customize FlameChart colors via colorMapper and colorHue

    main

    The FlameChart uses a color mapping system to visually distinguish different types of frames. You can customize this behavior using the colorMapper and colorHue properties.

    colorHue

    Set colorHue to a string (e.g., 'warm', 'red', 'blue') to apply a global color theme to all frames.

    colorMapper

    Provide a custom FrameColorGenerator<T> function to define colors based on the frame data itself.

    type FrameColorGenerator<T> = (frame: T, colorHue: string | null) => string;
    
    // Example: Custom color mapper
    chart.colorMapper = (frameData, defaultHue) => {
        if (frameData.isKernel) return '#ff0000';
        return defaultHue || '#cccccc';
    };
  9. Create an HTML raw text data printer with createHtmlRawTextDataPrinter

    main

    The createHtmlRawTextDataPrinter function generates a generator-based printer used to embed profile data as raw text within HTML reports. It wraps data chunks in <script> tags and includes a small inline script to process the data from the document.currentScript.previousSibling.text property. This is useful for embedding large datasets into HTML files without breaking the HTML structure (e.g., by accidentally including </script> sequences).

    Parameters

    • maxChunkSize (number, default: 1048576): The maximum size of a single chunk in bytes. If a chunk exceeds this size, the printer will split it.
    • type (string, default: 'unknown/data'): The type attribute for the <script> tag (e.g., 'application/json').
    • onDataChunk (string, default: ''): A JavaScript function body that will be executed immediately after the data chunk is embedded. This function receives the chunk text as its first argument.

    API Surface

    The returned object is a generator that provides two methods:

    • *push(chunk): A generator method that accepts a string chunk. It yields strings containing the wrapped data. It handles splitting chunks to avoid breaking the HTML script tags.
    • *finish(): A generator method that yields the final closing tags and any remaining buffered data.
    const createHtmlRawTextDataPrinter = require('./lib/html-data-printers/raw-text');
    
    // Example: Embedding JSON data
    const printer = createHtmlRawTextDataPrinter(
        1024 * 1024, 
        'application/json', 
        'const data = JSON.parse(chunk); console.log("Data loaded:", data);'
    );
    
    for (const output of printer.push('{"key": "value"}')) {
        console.log(output);
    }
    
    for (const output of printer.finish()) {
        console.log(output);
    }
  10. Extract and validate profile data with extractAndValidate()

    main

    The extractAndValidate function is the primary entry point for converting raw profile data into a standardized V8CpuProfileSet. It detects the input format, performs necessary transformations (like unrolling heads or normalizing profiles), and handles multiple profiles if enabled.

    Parameters

    • data: unknown: The raw input data (object, array, or trace).
    • rejectData: (reason: string, view?: unknown) => void: A callback function used to report validation errors or unsupported formats to the consumer.

    Behavior

    • Format Detection: It automatically identifies if the data is a DevTools Enhanced Trace, a Chromium Performance Profile, a V8 Log, or a standard CPU Profile.
    • Transformations: For standard CPU profiles, it performs internal normalization steps such as unrollHeadToNodesIfNeeded, unwrapSamplesIfNeeded, and convertParentIntoChildrenIfNeeded.
    • Error Handling: If the format is unknown or invalid, it calls rejectData and throws an Error.
    • Multi-profile Support: If FEATURE_MULTI_PROFILES is disabled, the function will only return the first valid profile found in the input.
    import { extractAndValidate } from './app/prepare/index.js';
    
    // Example usage:
    try {
        const result = extractAndValidate(rawData, (reason, view) => {
            console.error(`Validation failed: ${reason}`, view);
        });
        console.log('Processed profiles:', result.profiles);
    } catch (err) {
        console.error('Extraction error:', err.message);
    }