Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Ability to limit number of recorded function calls #171

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

zermelo-wisen
Copy link
Contributor

Fixes #162.

Summary

  • Added the ability to limit the number of recorded function calls, both total and per function. This approach ensures no orphaned calls are recorded, as the check is made at the start of the function call recording.

  • The limits can be configured via environment variables (APPMAP_MAX_RECORDED_CALLS & APPMAP_MAX_RECORDED_CALLS_PER_FUNCTION) and configuration file, with default values of 10,000,000 total calls and 100,000 calls per function.

  • Introduced a new test case that generates an AppMap with enforced total and per-function call limits.

Detailed Description

This PR introduces functionality and configuration updates to limit the number of function calls and total calls that can be recorded by the AppMap recorder.

File: src/Recording.ts

  • Added call count tracking:
    • Introduced callCountPerFunction and totalCallCount to track the number of calls per function and the total number of calls, respectively.
private callCountPerFunction = new Map<FunctionInfo, number>();
private totalCallCount = 0;
  • Implemented function call limit checks:
    • The method willExceedFunctionCallLimits was added to determine if the next function call would exceed predefined limits specified in the configuration.
public willExceedFunctionCallLimits(funInfo: FunctionInfo) {
  return (
    (config().maxRecordedCalls > 0 && this.totalCallCount >= config().maxRecordedCalls) ||
    (config().maxRecordedCallsPerFunction &&
      (this.callCountPerFunction.get(funInfo) ?? 0) >= config().maxRecordedCallsPerFunction)
  );
}
  • Modified functionCall method:
    • Updated to increment the call counters every time a function is recorded.
const count = this.callCountPerFunction.get(funInfo) ?? 0;
this.callCountPerFunction.set(funInfo, count + 1);
this.totalCallCount++;

File: src/config.ts

  • Added new default configuration values and environment variable names:
const maxRecordedCallsDefault = 10_000_000;
const kMaxRecordedCallsEnvar = "APPMAP_MAX_RECORDED_CALLS";

const maxRecordedCallsPerFunctionDefault = 100_000;
const kMaxRecordedCallsPerFunctionEnvar = "APPMAP_MAX_RECORDED_CALLS_PER_FUNCTION";
  • Updated Config class:
    • Introduced new properties to store the maximum recorded calls and maximum recorded calls per function.
public maxRecordedCalls: number;
public maxRecordedCallsPerFunction: number;
  • Initialized these properties in the Config constructor.
this.maxRecordedCalls =
  getNonNegativeIntegerEnvVarValue(kMaxRecordedCallsEnvar) ??
  config?.max_recorded_calls ??
  maxRecordedCallsDefault;

this.maxRecordedCallsPerFunction =
  getNonNegativeIntegerEnvVarValue(kMaxRecordedCallsPerFunctionEnvar) ??
  config?.max_recorded_calls_per_function ??
  maxRecordedCallsPerFunctionDefault;
  • Updated readConfigFile function:
    • Extended the parsing to include new configuration options max_recorded_calls and max_recorded_calls_per_function.
if ("max_recorded_calls" in config) {
  const value = parseInt(String(config.max_recorded_calls));
  result.max_recorded_calls = value >= 0 ? value : undefined;
}
if ("max_recorded_calls_per_function" in config) {
  const value = parseInt(String(config.max_recorded_calls_per_function));
  result.max_recorded_calls_per_function = value >= 0 ? value : undefined;
}

File: src/recorder.ts

  • Filtered function recordings:
    • Changed the record function to skip recordings that would exceed the specified function call limits.
const recordings = getActiveRecordings().filter((r) => !r.willExceedFunctionCallLimits(funInfo));

Testing

  • Added a new integration test for function call limits:
    • Created mapping a script using function call limits to validate the behavior of the new function call limits feature.
integrationTest.only("mapping a script using function call limits", () => {
  const options = {
    env: {
      ...process.env,
      APPMAP_MAX_RECORDED_CALLS: "5",
      APPMAP_MAX_RECORDED_CALLS_PER_FUNCTION: "2",
    },
  };
  expect(runAppmapNodeWithOptions(options, "callLimits.js").status).toBe(0);
  expect(readAppmap()).toMatchSnapshot();
});
  • Created callLimits.js for testing:
    • A simple script that calls functions a, b, and c multiple times to test the limits.
function a() {
  console.log("a");
}

function b() {
  console.log("b");
}

function c() {
  console.log("c");
}

a();
a();
a();

b();
b();
b();

c();
c();
c();

Added the ability to limit the number of recorded function calls, both
total and per function. This approach ensures no orphaned calls are
recorded, as the check is made at the start of the function call recording.

The limits can be configured via environment variables and configuration
file, with default values of 10,000,000 total calls and 100,000 calls per
function.

Introduced a new test case that generates an AppMap with enforced total and
per-function call limits.
@zermelo-wisen zermelo-wisen force-pushed the feat/limit-function-record-count branch from 36e0b7c to 6885987 Compare August 16, 2024 19:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Test hang in gchq/CyberChef
1 participant