#tree-sitter #static-analysis #command-line-tool #annotations #function #list #autometrics

bin+lib am_list

A tree-sitter-powered command line tool to list functions marked with Autometrics annotations

10 releases

0.3.0 Aug 29, 2023
0.2.7 Aug 16, 2023
0.2.6 Jul 27, 2023
0.2.2 Jun 19, 2023
0.1.0 May 29, 2023

#1194 in Database interfaces

MIT license

440KB
3.5K SLoC

Autometrics List

A command that lists all functions that have the "autometrics" annotation.

The aim is to use this binary as a quick static analyzer that returns from a codebase the complete list of functions that are annotated to be autometricized.

The analysis is powered by Tree-sitter, and all the specific logic is contained in Tree-sitter queries that are specific for each language implementation.

Quickstart

Use the installer script to pull the latest version directly from Github (change VERSION accordingly):

VERSION=0.2.0 curl --proto '=https' --tlsv1.2 -LsSf https://github.com/autometrics-dev/am_list/releases/download/v$VERSION/am_list-installer.sh | sh

And run the binary

# Make sure that `~/.cargo/bin` is in your `PATH`
am_list list -l rs /path/to/project/root

Current state and known issues

Language support table

In the following table, having the "detection" feature means that am_list returns the exact same labels as the ones you would need to use in PromQL to look at the metrics. In a nutshell, "Autometrics compliance".

Language Function name detection Module detection
Rust
Typescript ⚠️[^wrapper]
Go ⚠️[^all-functions]
Python
C#

[^wrapper]: For Typescript (and all languages where autometrics is a wrapper function), static analysis makes it hard to traverse imports to find the module where an instrumented function is defined, so the reported module is the module where the function has been instrumented

[^all-functions]: Support list all autometricized functions, but not all functions without restriction

Typescript

Module tracking

This tool cannot track modules "accurately" (meaning "the module label is exactly what autometrics will report"), because autometrics-ts uses the path of the source in the JS-compiled bundle to report the module. The compilation and bundling happens after am_list looks at the code so it cannot be accurate.

This means the module reporting for typescript is bound to be a "best effort" attempt to be useful.

The other difficulty encountered when using a static analysis tool with autometrics-ts is that the instrumentation can happen anywhere, as the wrapper function call can use an imported symbol as its argument:

import { exec } from "child_process";
import { autometrics } from "@autometrics/autometrics";

const instrumentedExec = autometrics(exec);

// use instrumentedExec everywhere instead of exec

In order to report the locus of function definition as the module, we would need to include both:

  • a complete import resolution step, to figure out the origin module of the instrumented function (child_process in the example), and
  • a dependency inspection step, to figure out the path to the instrumented function definition within the dependency (lib/child_process.js in the node source code)

This is impractical and error-prone to implement these steps accurately, so instead we only try to detect imports when they are explicitely imported in the same file, and we will only report the function module as the imported module (not the path to the file it is defined in). Practically that means that for this example:

// in src/router/index.ts
import { exec } from "child_process";
import { origRoute as myRoute } from "../handlers";
import { autometrics } from "@autometrics/autometrics";

const instrumentedExec = autometrics(exec);
const instrumentedRoute = autometrics(myRoute);

// use instrumentedExec everywhere instead of exec

am_list will report 2 functions:

  • {"function": "exec", "module": "ext://child_process"}: using ext:// protocol to say the module is non-local
  • {"function": "origRoute", "module": "handlers"}: even if myRoute is re-exported from ../handlers/my/routes/index.ts, we do not go look into what handlers did to expose origRoute; also, the alias is resolved.

Dependencies

~46MB
~1M SLoC