PMCPP(1)

πŸ’– PMCPP(1) β€” the manual page, but make it make sense πŸ’–

General Commands Manual Β· pmcpp Β· the cutest little preprocessor you ever did meet

okay bestie, sit down, hydrate, and let me tell you about pmcpp ✨


🌸 NAME 🌸

pmcpp β€” a simple preprocessor for the Performance Co-Pilot 🫢


πŸŽ€ SYNOPSIS πŸŽ€

pmcpp [-Prs?]  [-D name[=value] ...]  [-I dir ...]  [[infile] [outfile]]

✨ DESCRIPTION ✨

So pmcpp is this very simple pre-processor that was originally built to mess with Performance Metric Name Space (PMNS) files for the Performance Co-Pilot (PCP) β€” iconic origin story, honestly β€” but then it got generalized to also do conditional blocks, include-file processing, in-line shell command execution, and macro substitution for arbitrary files. glow-up of the century πŸ’…

Where you'll actually catch it in the wild: it's most commonly used internally to process the PMNS file(s) after pmLoadNameSpace(3) or pmLoadASCIINameSpace(3) gets called, and to pre-process the config files for pmlogger(1). iykyk 🌈

Here's the flow, bestie:

  • Input lines get read from infile β€” or from standard input if infile isn't specified.
  • They get processed.
  • They get written to outfile β€” or to standard output if outfile isn't specified.

All C-style comments of the form /* ... */ are stripped straight out of the input stream. gone. bye. πŸ’€

Also important: there are no predefined macros for pmcpp β€” nope, not one β€” although you can define macros on the command line with the -D option, where name and value have to follow the exact same rules as the #define directive described down below. no cap πŸ“Œ

pmcpp accepts the following directives in the input stream (just like cpp(1)) 🧚

  • #include "filename" or #include <filename>

Either way, the directory search path for filename tries filename first, then the directory of the command line infile (if there is one), followed by any directories named in -I command line arguments, and finally the $PCP_VAR_DIR/pmns directory β€” that last one is for backwards compatibility with earlier versions of pmcpp and the implied use from pmLoadASCIINameSpace(3). Also, #include directives may be nested, up to a maximum depth of 5. five. that's the limit, bestie, don't push it πŸ™ˆ

  • #shell "command" or #shell 'command'

The shell command gets executed and its standard output is inserted into the stream of data that pmcpp is processing. Functionally this is basically a #include directive, except the input lines come from a command instead of a file. The #shell directive is most useful for including or excluding #define or #undef directives based on run-time logic in the command. chef's kiss πŸ‘‘

  • #define name value or #define name "value" or #define name 'value'

Defines a value for the macro name, which must be a valid C-style name β€” so leading alphabetic or underscore, followed by zero or more alphanumerics or underscores. value is optional (and defaults to an empty string). There is no character escape mechanism, but either single quotes or double quotes may be used to define a value with special characters or embedded horizontal white space (no newlines, okay?). 🌸

  • #undef name

Removes the macro definition, if any, for name. gone girl πŸ’…

  • #ifdef name ... #endif or #ifndef name ... #endif

The enclosing lines get stripped or included, depending on whether the macro name is defined or not.

  • #else

Within a #ifdef or #ifndef block, #else may be used to delimit the lines to be included if the preceding ``if'' condition is false.

Macro substitution happens by breaking the input stream into words separated by white space or characters that are not valid in a macro name β€” i.e. not alphanumeric and not underscore. Each word gets checked, and if it matches a macro name, the word gets replaced by the macro value. Otherwise the word is left alone. simple as that πŸ¦‹

There is generally one output line for each input line, although the line may be empty if the text got stripped due to comment handling or conditional directives. And when there's a change in the input stream, an additional output line is generated of the form:

# lineno "filename"

which is telling you that the following line of output corresponds to line number lineno of the input file filename. cute little receipt, right? πŸŽ€


πŸ’… OPTIONS πŸ’…

The available command line options are:

  • -D name[=value], --define=name[=value] β€” Defines a macro with an optional value, as described earlier. ✨
  • -I dir, --include=dir β€” An additional directory to search for include files. πŸ—‚οΈ
  • -P β€” Suppresses the generation of the linemarker lines, described above. no more receipts πŸ’€
  • -s, --shell β€” Changes the expected input style from C-like to shell-like (where # is a comment prefix). This forces the following changes in pmcpp behaviour:
    • The control prefix character changes from # to %, so for example %include instead of #include, and %ifdef instead of #ifdef. πŸ”„
    • No C-style comment stripping is performed. πŸ™…
  • -r, --restrict β€” Provide finer control of macro expansion β€” this option restricts macro substitution to words that match the patterns #name or #{name} or, if -s is specified, then %name or %{name}. In this mode, the macro name alone in the input stream will never be expanded, however in control lines (like #ifdef) the macro name should appear alone, without the prefix character or the curly braces (refer to the EXAMPLES below). πŸ‘‘
  • -?, --help β€” Display usage message and exit. 🫢

Important cpp(1) features that are not supported by pmcpp include:

  • Macros with parameters β€” the pmcpp macros support only parameterless string substitution. 😭
  • #if expr ... #endif
  • Nested use of #ifdef or #ifndef.
  • Stripping C++ style comments, as in // comment.
  • Error recovery β€” the first error encountered by pmcpp will be fatal. πŸ’€
  • cpp(1) command line options like -o, -W, -U, and -x.

🌸 EXAMPLES 🌸

okay bestie, this is the part where it all clicks, i promise πŸ’–

example 1 β€” just plain pmcpp, no flags, vibing

command:

pmcpp

so if you feed it this on stdin:

#define MYDOMAIN 27

root {
    foo   MYDOMAIN:0:0
}

you get this back:

# 1 "<stdin>"


root {
    foo   27:0:0
}

see how MYDOMAIN got swapped for 27? the preprocessor ate the #define line and dropped that little # 1 "<stdin>" breadcrumb in its place ✨


for the next two examples, imagine there's a file called frequencies and it contains exactly these lines:

%define dk_freq 1min
%define cpu_freq '15 sec'

example 2 β€” pmcpp -rs, no extra defines

command:

pmcpp -rs

input:

# get logging frequencies
# e.g. dk_freq macro
%include "frequencies"

log mandatory on %dk_freq {
    disk.dev
}

# note no %want_cpu here
%ifdef want_cpu
%define cpu_pfx 'kernel.all.cpu.'
log mandatory on %cpu_freq {
    %{cpu_pfx}user
    %{cpu_pfx}sys
}
%endif

output:

# get logging frequencies
# e.g. dk_freq macro


log mandatory on 1min {
   disk.dev
}

# note no %want_cpu here





notice how %include "frequencies" pulled in dk_freq so %dk_freq became 1min πŸŽ€ and because nobody defined want_cpu, the whole %ifdef want_cpu block just... poofed πŸ’¨

example 3 β€” pmcpp -rs -D want_cpu, now we're cooking

command:

pmcpp -rs -D want_cpu

input (same as before, no cap):

# get logging frequencies
# e.g. dk_freq macro
%include "frequencies"

log mandatory on %dk_freq {
    disk.dev
}

# note no %want_cpu here
%ifdef want_cpu
%define cpu_pfx 'kernel.all.cpu.'
log mandatory on %cpu_freq {
    %{cpu_pfx}user
    %{cpu_pfx}sys
}
%endif

output:

# get logging frequencies
# e.g. dk_freq macro


log mandatory on 1min {
   disk.dev
}

# note no %want_cpu here


log mandatory on 15 sec {
   kernel.all.cpu.user
   kernel.all.cpu.sys
}

ok so NOW want_cpu is defined, so the %ifdef block stays in, %cpu_freq becomes 15 sec, and %{cpu_pfx} becomes kernel.all.cpu. β€” literally everything slotted into place πŸ‘‘


🌈 PCP ENVIRONMENT 🌈

so PCP has this whole thing where environment variables starting with PCP_ are the ones that parameterize the file and directory names PCP uses 🫢

every time you install it, the file /etc/pcp.conf holds the local values for all those variables. and if you wanna point at a different config file instead, you use the $PCP_CONF variable β€” full details live over in pcp.conf(5).

and if you're looking for the environment variables that affect PCP tools specifically (not the file/dir names), those are documented in pmGetOptions(3) πŸ’…


πŸ‘€ SEE ALSO πŸ‘€

go read these next, fr fr:


and that's the whole page bestie, you made it to the end πŸŽ‰ this is the Performance Co-Pilot PMCPP(1) manual page β€” iconic source, iconic version, and honestly the date is giving "whenever you're reading this" energy πŸ’– now go preprocess something and slay ✨

πŸ“¦ where this page comes from