Table of Contents
- User's Guide
- Reference
User's Guide
Introduction
This is the documentation for the Giopler system. It has been structured as a single web page for easier searching. It is a long document. Use the table of contents above to guide you. We recommend reading this document in order from the top, as needed. Click on the screenshots to enlarge them.
Giopler consists of a library providing an API to your source code, a server to collect event signals from your running code, and this website to report on the resulting metrics. We support all stages of software development such as development, profiling, and production. Our API includes calls for debugging, tracing, logging, profiling, software contracts, and monitoring.
We currently support C++ software development under Linux. This is expected to expand over time. Consult the Roadmap for more details.
Your program does not need to run with super user (root) privileges. We collect all signal metrics, including the Linux performance monitoring counters (PMCs), as a normal user of the computer system.
Familiarity with C++, Git, CMake, and general Linux software development tools is strongly recommended. Consult the list of Resources if you need to brush up. Although the client library makes extensive use of C++20 features internally, knowledge of this specific version is not required to use the system.
Quick Start
Getting up and running with Giopler is easy.
System Workflow
-
Include the Giopler header file into your source code.
-
Add the Giopler API calls to your source code as needed.
-
Compile your program, enabling the desired Giopler build mode.
-
Launch your program with the Giopler token defined as an environment variable.
-
Your program runs, sending signal events to the Giopler servers.
-
Our servers validate and index your data as we receive it.
-
At any time you can visit our website and run the reports.
System Requirements
Giopler for C++ is a header-only library. System requirements are modest.
- Linux Operating System
Linux is a great platform for running applications. It is by far the most popular choice for a server operating system. Any recent Linux distribution should be fine. We officially support the most recent Ubuntu long term support release.
Some of the database keys for your data are generated by your client application as the program runs. We use std::random_device as a source of entropy. Poor quality random keys could result in hash collisions within your data. This is an unlikely scenario but something to be aware of in unusual environments such as small embedded systems.
Our testing has focused on the x86_64 CPU architecture. In theory, any CPU fully supported by Linux (such as ARM) should also work. We will be expanding our testing to these other system architectures in the future.
- GCC or CLang
We test both of these compiler suites regularly. Gcc 13 and Clang 15 significantly improved the support for C++20, though they are not required. Gcc 11 and Clang 14 are also supported.
- OpenSSL
We use secure connections to communicate with the Giopler servers.
- Zlib library
We compress the data before sending it to the servers. This compression format is fully supported by modern web browsers.
- {fmt} library
The C++20 text formatting library is a game changer. Versions of Gcc and Clang before 13 and 15, respectively, require the stand-alone fmt library to be installed.
- Git version manager
Although not strictly necessary, using this tool will make it easier to retrieve the Giopler client library from GitHub, where it is hosted.
- CMake
We provide a simple CMakefile.txt
file to compile the library
and the sample applications. Feel free to use it if it looks useful.
Installing Requirements
Here are the Linux shell commands to install the system requirements for some popular Linux distributions. The commands are similar for other distributions.
# Ubuntu 22.04 LTS - jammy
# Zlib 1.2.11, Fmt 8.1.1, OpenSSL 3.0.2, CLang 14.0.0
# Gcc 11.2.0, CMake 3.22.1, Git 2.34.1
sudo apt-get install zlib1g-dev libfmt-dev openssl
sudo apt-get install clang gcc cmake git
# Fedora 37
# Zlib 1.2.12, Fmt 9.1.0, OpenSSL 3.0.5, CLang 15.0.4
# Gcc 12.2.1, CMake 3.25.1, Git 2.38.1
sudo dnf install zlib fmt openssl-dev
sudo dnf install clang gcc cmake git
# Arch - rolling release of December 12, 2022
# zlib 1.2.13, Fmt 9.1.0, OpenSSL 3.0.7, CLang 14.0.6
# Gcc 12.2.0, CMake 3.25.1, Git 2.38.2
sudo pacman -S zlib fmt openssl clang gcc cmake git
# OpenSUSE 15.4
# libz1 1.2.11, Fmt 8.0.1, OpenSSL 3.0.1, CLang 13.0.1
# Gcc 12.2.1, CMake 3.20.4, Git 2.35.3
sudo zypper install libz1 libfmt8 libopenssl3
sudo zypper install clang gcc12 cmake git
Installing the Giopler client library
The public home of the Giopler client library is on GitHub:
https://github.com/giopler/giopler-cpp
The easiest way to install it locally is to issue a Git command from a Linux shell:
git clone https://github.com/giopler/giopler-cpp.git
This will create a subdirectory called giopler-cpp
under the current directory.
Giopler Token
Before the Giopler servers can start accepting your event signals, you need to provide your running program a Giopler token. This is done by setting an environment variable called GIOPLER_TOKEN. This token identifies you as a valid user of your account. Every time you visit your Account page, a new access token is generated. Each token is valid for six months.
GIOPLER_TOKEN=<token> <program name> <program arguments>
Compiling your program
All you need to do is add the Giopler include directory to your include path. We can easily accommodate various build systems such as CMake or Make.
You need to tell Giopler what build mode you want to use at compile time.
We use this information to optimize the generated code.
Only one of these values can be set at a time.
You can either use an environment variable or a #define
from your code
before including the library. For example:
GIOPLER_BUILD_MODE_DEV=1 g++ <build instructions to compiler>
Build Mode | Define | Recd Build Type |
---|---|---|
Off | GIOPLER_BUILD_MODE_OFF | any |
Development | GIOPLER_BUILD_MODE_DEV | Debug |
Test | GIOPLER_BUILD_MODE_TEST | RelWithDebInfo |
Benchmark | GIOPLER_BUILD_MODE_BENCH | Release |
Profile | GIOPLER_BUILD_MODE_PROF | Release |
Q&A | GIOPLER_BUILD_MODE_QA | RelWithDebInfo |
Production | GIOPLER_BUILD_MODE_PROD | Release |
Sample Programs
The applications under the samples
subdirectory showcase the power
of the Giopler system. They are a great way to learn about the various features.
Feel free to run and modify the programs as needed.
Be aware some of these sample applications are quite complex in their execution. You should run simplified versions if you are concerned about the number of event signals generated.
Simple
As the name implies, this is an extremely simple application. It works great to make sure everything is setup correctly. It is also very easy to modify and to try out more elaborate uses of the library. As written, this program will send 6 events to the Giopler servers.
Hello
Showcases simple recursion. As written, this program will send 15 events to the Giopler servers.
There
Slightly more complex recursion. As written, this program will send 16 events to the Giopler servers.
Funnel_Sort
An implementation of a cache-oblivious sorting algorithm. The result is compared against the STL sort algorithm. As written, this program will send 8 events to the Giopler servers.
Threads
A simple test using multithreading. As written, this program will send 34 events to the Giopler servers.
Factorial
A test of the factorial sequence. As written, this program will send 49 events to the Giopler servers.
Ackermann
A test of the interesting Ackermann function. As written, this program will send 241 events to the Giopler servers.
Fibonacci
A test of the Fibonacci sequence. As written, this program will send 362 events to the Giopler servers.
Collatz
A test of the Collatz conjecture. Performs extensive recursion. As written, this program will send 398 events to the Giopler servers.
Matrices
A comparison of 10 matrix multiplication algorithms. The code goes through a lot of trouble to try and mitigate the factors that affect code profiling. The experience of writing this code was part of the motivation that led to the creation of the Giopler system. As written, this program will send 24 events to the Giopler servers.
Canonical Function
It is up to you to decide how Giopler can best help you. Focus on the hot spots. You get program-level and thread-level events by just including the Giopler header, so start with that. Add API calls slowly as needed. Here is a sample "canonical" function which makes extensive use of Giopler APIs.
#include <string>
#include "giopler/giopler.hpp"
int add_one(int value)
{
giopler::dev::Id id(std::to_string(value));
giopler::dev::Class clss("math");
giopler::dev::Function function(value);
giopler::dev::argument(value >= 0);
return value+1;
}
Why Giopler is different
Compared to Sampling Profilers
Giopler is a deterministic system. This means that counts of function calls, for example, are exact and not somehow estimated. Sampling profilers take snapshots of the running program and cannot measure the number of times a function was called, for example. This is critical information that you are somehow expected to do without.
We are not affected by the optimization level of the compiler. Inlining works against what a sampling profiler is trying to accomplish. We only trace and profile functions you know are important. Sampling profilers will report on absolutely every function that executed, making for very noisy output.
Are some of the calls to a function slow, while on average they are fast? Are they slow because you are asking them to do more work than usual? Giopler can show you why. Sampling profilers cannot.
Compared to Production Monitoring Tools
Giopler is first and foremost a tool designer by and for software engineers. Our primary focus is to present computer programmers with actionable information, so they can make the software they write better with less effort.
There are a large number of tools available for monitoring running programs in a production environment and for recording their log output. The audience for these tools is the development operations ("dev ops") department. Giopler has some overlap with these tools, but our focus is clearly different.
Compared to other debuggers
Our structured logging of trace data goes far beyond what a printf
approach can do. With the Class and Id feature, you
can see the big picture and tie the code execution with the work being accomplished.
Stepping through code with a debugger works best when you have a good idea of the source of a problem to begin with.
Giopler fully indexes all your event data as soon as we receive it. You can easily filter and sort the data to
quickly focus in on an issue.
Reports
The reports on the dashboard work with live data as soon as it is received and processed. This normally happens within a few milliseconds. You do not need to wait until your program has finished running before displaying its data.
The data level menu items (Summary, Program, Run) are for overview reports across runs. The rest of the menu items are for detailed looks inside a given run. Each build mode has different goals, so the reports available differ.
A primary design goal of the Giopler system architecture is the speed of the reports. We extensively pre-aggregate data and cache results in the servers and the web browser as needed. Do not be surprised if you request a report and the results are available to you instantly. This is entirely normal and not some sort of system error.
Data Notes
Durations are always stored as seconds using double precision. They are converted into appropriate units before display. But the data is exported as native values of seconds. A duration of zero for a run indicates that it is still running, has aborted, or our servers are still processing the data.
Summary-level reports aggregate data by day or month. We use UTC for the timezone to determine when a day or month ends. A side effect of this is that the way the data is summarized in these reports may not match your recollection of when it was generated.
On Linux, each system thread of execution within a process is treated equally.
Linux does not track which thread created which other threads.
The start of execution of each thread is marked with a function call named <thread>
.
We do not mark these function calls as roots, since doing so would not be very useful.
Similarly, icicle charts mark the bottom of a chain of function calls with a
fictitious function call named <leaf>
. This is added to make visualizing
the data easier and does not affect any metrics.
There may be a short delay the first time a given report is requested. The servers will cache the computed data, making future requests for the same report much faster. The servers will not cache the data if it is determined the program is still running.
Modern web applications perform extensive and sophisticated caching of data in the web browser. Giopler is no different. For best results, wait until a run has completed before visualizing its data. You can also try and force your web browser to refresh all the data from the Giopler website. The procedure varies by web browser and operating system and is beyond the scope of this guide. The instructions on the Refresh Your Cache (https://refreshyourcache.com/) website might be helpful.
Report Types
Line Chart
Line chart reports are good for quick overviews. They let you see trends at a glance without too much clutter or detail. They are also very quick to display. If they contain a timeline, you can use the date selection fields at the bottom to zoom into a specific time range.
Table
Table reports are best for displaying lots of variables and lots of data. Rows can be sorted by clicking on the arrows on each column. Columns within groups can be reordered. Rows can be filtered by entering a value into the filter row. Use the buttons below the table to export the table data in Csv, Xlsx, or PDF formats.
Table reports load the row data on demand as you scroll. Sorting is handled client-side within the web browser. Only data that has already been loaded will be sorted. Because of the dynamic loading of table data, the vertical scrollbar can jump around as the table is populated with new data.
There is an issue with the table component when rows are loaded on demand as you scroll. You are (incorrectly) brought back up to the first row. The rows may also seem to disappear from the component. If you start scrolling again, the rows will reappear. If you scroll to the bottom of the table, you will see the newly loaded rows. This issue has been reported. We are awaiting a fix or a workaround.
Histogram
Histograms let us visualize the variability of a metric. It is not uncommon for a function call to take ten times the runtime of the average once in a while. Why is that happening, and what can you do about it?
The number of bins used is computed using a heuristic. We take into account the number of function calls and the dispersion of the data. The heuristics is called the Freedman-Diaconis rule.
We normally display the average (arithmetic mean) of metrics as a quick way to summarize the values. The mean value we display with histograms is computed using a much more sophisticated algorithm. These values are more robust (less affected by noise in the data) and more efficient (more representative of all the data values). Our algorithm is proprietary, but you can get an idea of what we are doing by looking at the trimean over at the Wikipedia. This value is graphically represented in the chart as a red vertical dashed line.
Histograms additionally support filtering the function data to a specific time range during the program's run. This is very useful, for example, to see if the slow function executions happened mostly during the initialization phase of the run. The initial range displayed includes every execution of the function.
Icicle Chart
Icicle chart reports are for visualizing function call chain hierarchies. Click on the chart to navigate up and down the call chain. Color is an indication of the maximum depth to the right from that function call. We omit the function template arguments for the sake of brevity. The tooltip contains the path down from the left side of the graph. The bottom of the chart contains a short legend of the contents of each entry.
A limited version of the icicle chart is available in development builds. We do not have timing data to display, but we can show the number of function calls.
Selection Fields
Finally, a quick note about selection fields. These are used for selecting a program or a run, for example. Since the potential number of items to display could number in the thousands, we use a virtual on-demand list. We initially load a few hundred entries (enough most of the time). We also support full-text search on the text fields of the entries. For a run you can type in text such as the host name or username that ran the program.
SI Scaling Suffixes
Very small and very large counter values are scaled and given a suffix according to the following table.
Suffix | Abbrev | Scale |
---|---|---|
y | yocto | 10⁻²⁴ |
z | zepto | 10⁻²¹ |
a | atto | 10⁻¹⁸ |
f | femto | 10⁻¹⁵ |
p | pico | 10⁻¹² |
n | nano | 10⁻⁹ |
µ | micro | 10⁻⁶ |
m | milli | 10⁻³ |
(none) | 10⁰ | |
k | kilo | 10³ |
M | mega | 10⁶ |
G | giga | 10⁹ |
T | tera | 10¹² |
P | peta | 10¹⁵ |
E | exa | 10¹⁸ |
Z | zetta | 10²¹ |
Y | yotta | 10²⁴ |
Duration Suffixes
Very small and very large time duration values are scaled and given a suffix according to the following table.
Suffix | Abbrev | Scale |
---|---|---|
μs | microseconds | 10⁻⁶ |
ms | milliseconds | 10⁻³ |
s | seconds | 10⁰ |
min | minutes | 60 |
hr | hours | 60*60 |
Frequently Asked Questions
Here are answers to questions you may have which are unrelated to the rest of this documentation.
Which compiler suite should I use, Gcc or Clang?
It used to be, you picked Clang for the better error messages and you picked Gcc for the better generated code quality. As a recent comparison by Phoronix demonstrates, this is no longer quite true. Today, these two compilers are far more alike than different.
We run both compilers during our testing. They do not always agree on what is correct code and what is the cause of a compilation error. For that reason, we recommend running both.
What C++ coding standard is followed in the client?
Hmmm. We call it "whatever feels right, as long as it is consistent."
Why the choice of license for the client library?
Choosing an open source license is a surprisingly difficult decision. After much careful deliberation, we decided to use the Creative Commons Attribution-NoDerivatives license. Here is the reasoning.
The client library is designed to work in lock-step with the Giopler servers. C++ is a complex programming language and, despite our attempts to write clean and concise code, there is much code logic in the library that may not be immediately obvious. Any changes made to the client library are bound to have unintended and unknowable consequences if it ends up sending unexpected data to our servers. For security purposes, the servers are designed to silently discard any data that does not match the exact format they are looking for.
The applications under the sample
subdirectory
are licensed under the less restrictive MIT license.
How can I get profiling to work when I run my code in the cloud?
When you run your code inside a virtual environment, you are at the mercy of what that environment permits. The cloud vendors (Amazon AWS, Google Cloud Platform, Microsoft Azure, etc) vary in their support of Linux's profiling API. AWS, for example, exposes the API to the virtual machine only on their larger EC2 instances.
Resources
There are many free fantastic resources online for improving your computer programming skills. Below are some of our favorites. Feel free to contact us if we've left any of your favorites out.
Learning Resources Online
- C++ Reference (https://cppreference.com/)
- Learn C++ (https://www.learncpp.com/)
- C Programming (https://www.cprogramming.com/)
- C++ FAQ (https://isocpp.org/faq)
- C++ Core Guidelines (https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines)
- Bjarne Stroustrup (https://www.stroustrup.com/)
- Linux Kernel (https://docs.kernel.org/)
Online Tools
Tools
- Visual Studio Code editor (https://code.visualstudio.com/docs/languages/cpp)
- CMake build tool (https://cmake.org/)
- Git version control system (https://git-scm.com/)
- fmt formatting library (https://fmt.dev/)
- Zlib compression format (https://www.zlib.net/)
- OpenSSL cryptography library (https://www.openssl.org/)
Compilers
- CLang (https://clang.llvm.org/)
- C++ Standards Support in CLang (https://clang.llvm.org/cxx_status.html)
- GCC GNU Compiler Collection (https://gcc.gnu.org/)
- C++ Standards Support in GCC (https://gcc.gnu.org/projects/cxx-status.html)
Books
- A Tour of C++
- Effective Modern C++
- C++ Primer
- Programming: Principles and Practice Using C++
- C++ Crash Course
Implementation Notes
Here are some technical details not strictly necessary to understand to use the system.
- C++ is a complex programming language. Support for the latest standard, C++20, is still a work in progress. Giopler is a modern and sophisticated library. Be mindful of hitting corner cases we have missed in our testing.
- When disabled by the build mode, function API calls (like
expect
) incur zero runtime cost. There is no code generated and there is no additional space used. - When disabled by the build mode, class API calls (like
Invariant
) incur a minimal runtime cost. An emptystd::unique_ptr
is created but not used. It typically occupies 8 bytes of RAM. - We give CPU priority to the running program. Programs that generate a large number of signal events quickly will develop a backlog waiting to be processed. This is normal. Please do not forcefully terminate the program and wait for it to exit normally. Profiling data will be of limited value in program runs that terminate prematurely.
Roadmap
This is a live document. The roadmap will be updated often to reflect our best understanding of what we are working on and where we are headed. We welcome your feedback!
Completed
- (June 1, 2023) system testing
- (June 10, 2023) public launch of beta
- (August 1, 2023) general availability
Short Term (0-6 months)
- add more reports based on feedback
- expand documentation based on feedback
Medium Term (6-12 months)
- add support for a client-side proxy server
- add support for running benchmarking unit tests
- add support for general unit tests
Long Term (1 year or more)
- C client
- Rust client
- Windows client support
- FreeBSD client support
- macOS client support
- Java client
- Python client
Reference
This reference section is intended to be used as specific questions come up while learning the Giopler system. Soon, using Giopler should become second nature and all you will need are occasional looks at the reference card.
Reference Cards
Lowercase entries refer to functions. Capitalized entries refer to classes.
Contract | Off | Dev | Test | Bench | Prof | QA | Prod |
---|---|---|---|---|---|---|---|
dev::argument | ✕ | ✓ | ✓ | ✕ | ✕ | ✓ | ✕ |
dev::expect | ✕ | ✓ | ✓ | ✕ | ✕ | ✓ | ✕ |
dev::confirm | ✕ | ✓ | ✓ | ✕ | ✕ | ✓ | ✕ |
dev::Invariant | ✕ | ✓ | ✓ | ✕ | ✕ | ✓ | ✕ |
dev::Ensure | ✕ | ✓ | ✓ | ✕ | ✕ | ✓ | ✕ |
prod::certify | ✕ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
Trace | Off | Dev | Test | Bench | Prof | QA | Prod |
---|---|---|---|---|---|---|---|
prod::Id | ✕ | ✓ | ✓ | ✕ | ✓ | ✕ | ✓ |
prod::Class | ✕ | ✓ | ✓ | ✕ | ✓ | ✕ | ✓ |
prod::uuid | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
dev::line | ✕ | ✓ | ✕ | ✕ | ✕ | ✕ | ✕ |
dev::breakpoint | ✕ | ✓ | ✕ | ✕ | ✕ | ✕ | ✕ |
prod::branch | ✕ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
Log | Off | Dev | Test | Bench | Prof | QA | Prod |
---|---|---|---|---|---|---|---|
dev::warning | ✕ | ✓ | ✓ | ✕ | ✕ | ✕ | ✕ |
prod::error | ✕ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
prod::message | ✕ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
Profile | Off | Dev | Test | Bench | Prof | QA | Prod |
---|---|---|---|---|---|---|---|
dev::Program | ✕ | ✓ | ✕ | ✓ | ✓ | ✕ | ✕ |
dev::Thread | ✕ | ✓ | ✕ | ✓ | ✓ | ✕ | ✕ |
dev::Function | ✕ | ✓ | ✕ | ✓ | ✓ | ✕ | ✕ |
dev::Object | ✕ | ✓ | ✕ | ✕ | ✓ | ✕ | ✕ |
Environment Variables
Define these in your shell command processor or integrated development environment (IDE) before starting your application.
GIOPLER_TOKEN
The Giopler token identifies you to our server. It is required. A given token is valid for six months from the date it was generated. You will receive an email everytime you use a token that is more than five months old. We recommend generating a new token every three months. You can find new tokens on the account page.
GIOPLER_QUIET
Define this to turn off Giopler status messages. Note that we use multiple threads to send events to the Giopler system. The transfer rate displayed by these messages (events/second) refers to only one thread. To estimate the global transfer rate, multiply these values by the number of event processing threads.
GIOPLER_LOCAL
Define this to use a local server (http://127.0.0.1:3000/). Used for system development. Not for use by customers.
GIOPLER_PROXY_HOST
Not implemented yet. Will be used for the proxy hostname.
GIOPLER_PROXY_PORT
Not implemented yet. Will be used for the proxy port number.
Build Modes
Define one of these in the environment or the makefile to control the build mode of the library.
- GIOPLER_BUILD_MODE_OFF
Disable all the features. Minimize the runtime impact of the code as much as possible.
- GIOPLER_BUILD_MODE_DEV
Software development and debugging mode. Turns on all runtime checks and logging output.
- GIOPLER_BUILD_MODE_TEST
Run the unit tests to validate the correct behavior.
- GIOPLER_BUILD_MODE_BENCH
Run the profiling tests to validate and track over time the runtime performance.
- GIOPLER_BUILD_MODE_PROF
Profiling mode. Tries to match the production mode performance while collecting performance metrics.
- GIOPLER_BUILD_MODE_QA
Disables tracing and informational/logging messages. Disables profiling. Enables contract checks.
- GIOPLER_BUILD_MODE_PROD
Production mode. Reduced runtime checks and logging.
Application Programming Interface (API)
The API is divided into modules according to their primary purpose. The behavior of the functions or classes changes depending on the build mode. Sometimes they are disabled by the build mode and at other times what they do might be different.
Contract
The contract functions and classes are for ensuring correct program behavior. Tracing events are generated in development mode if the contract condition is fulfulled. They generate an event and throw an exception when their contract is broken. Before throwing the exception we make an effort to send to the Giopler server any queued events. The exception thrown is derived from logic_error:
class contract_violation : public std::logic_error;
Use these for ensuring the quality of your software. Their runtime cost is low when they are not triggered.
argument
void giopler::dev::argument(const bool condition);
Validate the argument value of a function. Normally placed at the beginning of a function. Condition equal to true means no action is required.
expect
void giopler::dev::expect(const bool condition);
A general precondition on a function. Use these to validate assumptions about the environment on function entry. Condition equal to true means no action is required.
confirm
void giopler::dev::confirm(const bool condition);
To be used anywhere inside a function where any condition needs to be validated. Useful at the beginning or end of conditional blocks (if or while statements). Condition equal to true means no action is required.
Invariant
class giopler::dev::Invariant(BoolFunction auto condition_function);
BoolFunction is a concept for a function without parameters that returns a boolean.
Invariants are conditions that must always be true for proper operation of a class.
A good use for these is to declare a variable at the beginning of a function.
The condition will then be checked on function entry and function exit.
Condition function equal to true means no action is required.
Declare variables of this class using [[maybe_unused]]
to avoid compiler warnings
about unused variables.
Ensure
class giopler::dev::Ensure(BoolFunction auto condition_function);
BoolFunction is a concept for a function without parameters that returns a boolean.
Ensure is a limited version of Invariant.
It only checks the condition at scope exit.
Useful to make sure the function worked as expected.
Condition function equal to true means no action is required.
Declare variables of this class using [[maybe_unused]]
to avoid compiler warnings
about unused variables.
certify
void giopler::prod::certify(const bool condition);
A general precondition on a function. These conditions will always be checked, even in production build mode. These are conditions that are critical to proper program operation. Condition equal to true means no action is required.
Trace
The trace API is for marking points of interest in the code. These are not necessarily warning or error conditions. Think of these as "where" conditions. The location where they occurred is crucial to understanding what happened.
Id
class giopler::prod::Id(std::string_view id_value);
Set a thread-local identifying value. Intended to be used for unique values.
Inspired by the HTML id
attribute.
It does not directly generate signal events.
They define values that are then included in all the events signalled.
This is a lightweight class with very little overhead.
It should be defined before Function, so its value can be included in the Function event.
Example
giopler::prod::Id("cust.12345");
Class
class giopler::prod::Class(std::string_view class_value);
Set a thread-local class value. Intended to be used for categorical values.
Inspired by the HTML class
attribute.
Note that class
is a reserved keyword in C++. We recommend using clss
for the local variable name.
It does not directly generate signal events.
They define values that are then included in all the events signalled.
This is a lightweight class with very little overhead.
It should be defined before Function, so its value can be included in the Function event.
Example
giopler::prod::Class clss("disk.write.widget");
uuid
std::string giopler::prod::uuid();
Generates a random UUID. This is a stand-alone function not dependent on the rest of the Giopler client library. Useful to generate unique id values for the Id class, for example.
Example
// for example, "015971ac-d43f-4a64-a1e1-25b3b60af7dd"
giopler::prod::Id(giopler::prod::uuid());
line
void giopler::dev::line();
void giopler::dev::line(const std::string_view message);
void giopler::dev::line(StringFunction auto message_function);
StringFunction is a concept for a function without parameters that returns a string.
Logs that a given line in the source code was executed. The optional message string or message function adds a custom message to the event log.
breakpoint
void giopler::dev::breakpoint();
Adds a debugger breakpoint at the point the function was invoked. This is very platform and processor specific. The list of supported systems will grow over time.
branch
void giopler::prod::branch();
void giopler::prod::branch(const std::string_view message);
void giopler::prod::branch(StringFunction auto message_function);
StringFunction is a concept for a function without parameters that returns a string. Use this function to document key decision points in the code. The function will execute in all build modes, including production mode.
Log
The log API is for signaling potentially serious conditions in the code. Think of these as "what" conditions. They signal something of interest, not necessarily related to where the condition is signaled.
warning
void giopler::dev::warning();
void giopler::dev::warning(const std::string_view message);
void giopler::dev::warning(StringFunction auto message_function);
StringFunction is a concept for a function without parameters that returns a string. Signal an anomalous condition in the code. The audience for these messages is software developers.
error
void giopler::prod::error();
void giopler::prod::error(const std::string_view message);
void giopler::prod::error(StringFunction auto message_function);
StringFunction is a concept for a function without parameters that returns a string. Log an error condition in the code. These are always enabled and should always be looked at. It does not throw an exception, since your code has better context. We recommend your code throw an exception immediately after signaling this event.
message
void giopler::prod::message();
void giopler::prod::message(const std::string_view message);
void giopler::prod::message(StringFunction auto message_function);
StringFunction is a concept for a function without parameters that returns a string. They are not necessarily warnings or errors. Similar to the branch function, these can be used to signal important milestones while running the program.
Profile
Function
class giopler::dev::Function;
class giopler::dev::Function(const double workload);
Trace or profile a function. Should be placed at the beginning of the function.
Workload is an indication of the amount of work done by the function.
Declare variables of this class using [[maybe_unused]]
to avoid compiler warnings
about unused variables.
Object
class giopler::dev::Object;
Track the lifetime of an object. Should be declared inside a class or struct.
Declare variables of this class using [[maybe_unused]]
to avoid compiler warnings
about unused variables.
Data Dictionary
It is a significant challenge translating a complex multidimensional data structure into something that can be easily understood. Treat this as a snapshot in time of some of the data collected from the running programs.
In the following tables, field names that are boldfaced indicate text fields. These fields participate in full-text searches from the selection fields.
Program
- program - string - program name
- first_start - timestamp - earliest start timestamp
- last_start - timestamp - most recent start timestamp
Run
These values are constant for the duration of the program run.
- program_name - string - system program name
- start_ts - timestamp - when program started running
- end_diff - real - duration of program run in seconds
- event_cnt - integer - total number of events recorded in this run
- build_mode - string - Off, Dev, Test, Bench, Prof, Qa, Prod
- memory_page_size - integer - memory page size (bytes)
- physical_memory - integer - physical memory size (bytes)
- conf_cpu_cores - integer - number of configured CPU cores
- available_cpu_cores - integer - number of available CPU cores (powered-on/plugged-in)
- process_id - integer - system process id (/proc/sys/kernel/pid_max)
- build_mode - string - dev, test, prof, qa, prod
- compiler - string - Gcc, Clang, Microsoft, Intel
- platform - string - Linux, Windows, Bsd
- architecture - string - x86_64
- host_name - string - name of current host
- real_username - string - logged-in username
- effective_username - string - username that the process is running under
Event
These values could change as the program runs. They are collected with each event signal.
- event_category - string - contract, dev, prof, test, prod
- event_name - string - identifies the event
- file - string - source file name and path
- line - integer - line number
- func - string - function name and signature
- time_diff - real - when event occurred, delta from run start time
- thread_id - integer - system thread id
- node_id - integer - NUMA node id where thread is currently running
- cpu_id - integer - CPU core id where thread is currently running
- available_memory - integer - free memory size (bytes)
- cur_freq - integer - current CPU core actual frequency (kHz)
- max_freq - integer - current CPU core maximum frequency (kHz)
- load_avg1 - real - number of processes in the system run queue divided by the number of available CPU cores, averaged over one minute
- load_avg5 - real - number of processes in the system run queue divided by the number of available CPU cores, averaged over five minutes
- load_avg15 - real - number of processes in the system run queue divided by the number of available CPU cores, averaged over 15 minutes
- message - string - additional event details
- workload - real - user-assigned weight to profiled function calls
- class - string - tagged class name
- id - string - tagged id string
- status - string - Passed or Failed - for contracts and error/warning
Linux Performance Monitoring Counters (PMC)
duration
Real (wall clock) duration (secs)
Computed
These synthetic metrics might be helpful in deriving useful insights.
Cycles per Instruction (Cyc/Instr)
Total number of CPU cycles / Retired instructions. Lower values are better. An indication of how efficiently your instruction pipeline is working. A reminder that a suffix of 'm' means the value has been scaled up by 1000.
Front-End Stall Cycle Ratio (Frnt Stall)
Stalled cycles during issue in the frontend / Total cycles. Lower values are better. An indication of how efficiently your instruction pipeline is working. A reminder that a suffix of 'm' means the value has been scaled up by 1000.
Back-End Stall Cycle Ratio (Back Stall)
Stalled cycles during retirement in the backend / Total cycles. Lower values are better. An indication of how efficiently your instruction pipeline is working. A reminder that a suffix of 'm' means the value has been scaled up by 1000.
Cache Miss Ratio (Cache Miss)
Cache misses / Cache accesses. Lower values are better. An indication of how well the cache predictor is working with your code. A reminder that a suffix of 'm' means the value has been scaled up by 1000.
Mispredicted Branch Ratio (Brnch Miss)
Mispredicted branch instructions / Retired branch instructions. Lower values are better. An indication of how well the branch predictor is working with your code. A reminder that a suffix of 'm' means the value has been scaled up by 1000.
Software
These counters are generated by the Linux operating system itself.
CPU clock
high-resolution per-CPU timer. (secs)
Thread clock
Clock count specific to the task that is running. (secs)
Context switches
Counts context switches. This is when Linux stores the CPU state for a task, so it can be resumed later.
CPU migrations
Number of times the process has migrated to a new CPU.
Total memory page faults
Total number of memory (RAM) page faults.
Major memory page faults
Number of major page faults. These required disk I/O to handle.
Minor memory page faults
Number of minor memory page faults.
Memory alignment faults
Counts the number of alignment faults. Zero on x86 architecture.
Emulation faults
Counts the number of emulation faults.
Hardware
These counters come directly from the CPU hardware. Linux normalizes them, minimizing differences between hardware architectures.
Total cycles
Total CPU cycles
Retired instructions
Retired instructions (i.e., executed)
Stalled front-end cycles
Stalled CPU cycles during issue in the frontend.
Stalled back-end cycles
Stalled CPU cycles during retirement in the backend.
Retired branches
Retired branch instructions (i.e., executed).
Mispredicted branches
Mispredicted branch instructions
Cache accesses
Cache accesses. Usually this indicates Last Level Cache accesses.
Cache misses
Cache misses. Usually this indicates Last Level Cache misses.