fTimer Troubleshooting
June 8, 2026 ยท View on GitHub
When to read this: When a first build, MPI summary/report, CSV export, or OpenMP timing attempt fails and you want the practical next step. This is a troubleshooting guide, not a replacement for the full runtime contract in
docs/semantics.md.
fTimer Troubleshooting
Start with the smallest build that exercises what you need:
cmake -B build-smoke
cmake --build build-smoke
cmake -E chdir build-smoke ctest --output-on-failure
For the first example only:
cmake -B build-smoke
cmake --build build-smoke --target basic_usage
./build-smoke/examples/basic_usage
That path should not require MPI or pFUnit. If it fails, fix the serial build first before adding MPI, OpenMP, or the pFUnit suite.
Configure Cannot Find A Fortran Compiler
CMake needs a Fortran compiler with preprocessing support. Install or load a validated compiler, then configure with that compiler explicitly:
FC=gfortran cmake -B build-smoke
GNU Fortran and LLVM Flang are the validated serial smoke paths. Other serial compilers may work, but they are outside the current release-validated matrix.
Configure Fails After Switching Compilers Or Modes
CMake build trees are not portable across compiler or mode changes. A build
directory configured with gfortran should not be reused for mpifort,
flang-19, FTIMER_USE_MPI=ON, or FTIMER_USE_OPENMP=ON.
Use a separate build directory:
FC=gfortran cmake -B build-smoke
FC=mpifort cmake -B build-mpi-smoke -DFTIMER_USE_MPI=ON
FC=gfortran cmake -B build-openmp -DFTIMER_USE_OPENMP=ON
For a clean reconfigure, remove the old build directory or use a fresh one.
With CMake 3.24 or newer, cmake --fresh is also available as a convenience.
Configure Fails With pFUnit Enabled
FTIMER_BUILD_TESTS=ON enables the pFUnit behavioral suite. That requires a
pFUnit installation built for the same compiler and, for MPI tests, the same MPI
toolchain family.
Pass the install prefix explicitly:
FC=gfortran cmake -B build -DFTIMER_BUILD_TESTS=ON -DPFUNIT_DIR=/path/to/pfunit
If you only want the smoke path and examples, leave FTIMER_BUILD_TESTS=OFF.
The default smoke build does not require pFUnit.
Downstream Configure Cannot Find fTimer
Downstream projects should consume the installed CMake package with
find_package(fTimer CONFIG REQUIRED). If CMake reports that it cannot find
fTimerConfig.cmake, point CMAKE_PREFIX_PATH at the prefix where fTimer was
installed:
cmake -B build-install -DCMAKE_INSTALL_PREFIX=/path/to/ftimer-install
cmake --build build-install
cmake --install build-install
cmake -S my_app -B my_app/build -DCMAKE_PREFIX_PATH=/path/to/ftimer-install
Check that the selected prefix contains the package files, usually under
lib/cmake/fTimer/, and the Fortran module artifacts under include/ftimer/
unless CMAKE_INSTALL_INCLUDEDIR was changed. If you switched compilers or
feature modes, rebuild and reinstall into a fresh prefix so the downstream
project does not mix stale module files with a new compiler or MPI/OpenMP mode.
The smoke suite verifies this contract through the installed-package consumer:
cmake -B build-smoke
cmake --build build-smoke
cmake -E chdir build-smoke ctest --output-on-failure -R ftimer_installed_package_consumer$
Configure Fails With MPI Enabled
FTIMER_USE_MPI=ON is intended for MPI wrapper compilers such as mpifort.
Configure runs a small mpi_f08 probe and fails early if the active compiler
cannot consume the discovered MPI module files.
Use a wrapper from the same toolchain as the MPI installation:
FC=mpifort cmake -B build-mpi-smoke -DFTIMER_USE_MPI=ON -DFTIMER_BUILD_TESTS=OFF
If the error mentions mpi_f08, MPI_Type_match_size, or
MPI_ERRORS_RETURN, check that:
FCorCMAKE_Fortran_COMPILERpoints at the MPI wrapper you meant to use.- The wrapper belongs to the MPI installation CMake discovered.
- The MPI implementation provides the
mpi_f08interfaces fTimer validates.
The supported communicator interface is type(MPI_Comm) from mpi_f08.
Legacy integer communicator handles and mpif.h are not supported paths.
Configure Fails With OpenMP Enabled
FTIMER_USE_OPENMP=ON is currently validated for GNU Fortran and LLVM Flang.
The configure step rejects unvalidated compiler IDs before trying to discover
the OpenMP runtime.
Use one of the documented paths:
FC=gfortran cmake -B build-openmp -DFTIMER_USE_OPENMP=ON
FC=flang-19 cmake -B build-openmp-flang -DFTIMER_USE_OPENMP=ON -DOpenMP_ROOT=/path/to/libomp
LLVM Flang OpenMP validation requires CMake 3.24 or newer so CMake reports the
compiler ID as LLVMFlang. If runtime execution is unavailable because you are
cross-compiling or packaging in a restricted environment,
FTIMER_OPENMP_ASSUME_MASTER_PROBE_OK=ON is an advanced maintainer escape hatch
and should be used only after independently validating equivalent OpenMP
master-thread and worker-lane semantics.
OpenMP Worker Calls Do Not Appear In The Summary
For the existing ftimer and ftimer_core APIs, FTIMER_USE_OPENMP=ON enables
a master-thread-only compatibility carve-out. Worker-thread calls made inside an
OpenMP parallel region are silent no-ops: no summary row, no call-count
increment, and no changed ierr.
To time a parallel region as one wall-clock interval, place start and stop
outside the !$omp parallel block. To record worker-lane participation, use the
explicit ftimer_openmp_t API with registered ids and an explicitly opened
timed region. See docs/openmp-timing-modes.md.
Global compiler flags such as -fopenmp do not enable fTimer's guards when the
library was configured with FTIMER_USE_OPENMP=OFF. The CMake option is the
source-level switch for the guard code and the supported worker-timing runtime.
MPI Checklist
Before debugging the output, confirm the lifecycle and communicator contract:
- Call MPI-enabled fTimer only after
MPI_Initand beforeMPI_Finalize. - Finish fTimer MPI summaries, reports, CSV writes,
finalize(), and any reinitialization before freeing a caller-owned communicator. - Keep
init(comm=...)communicators valid; fTimer stores borrowed, non-owning handles and does not callMPI_Comm_duporMPI_Comm_free. - Have every rank in the captured communicator enter each MPI summary/report collective.
- Stop all timers before MPI summary/report calls. Local summaries can snapshot active timers; MPI summaries are stopped-run-only.
- Use strict MPI summary/report APIs only when all participating ranks have the same descriptor tree.
- Use sparse union APIs for rank-conditional timer trees.
fTimer does not insert MPI barriers around timed regions. It reduces rank-local wall-clock intervals. If you need a synchronized global phase, add the barrier or synchronization you intend to measure in application code.
MPI Summary Returns FTIMER_ERR_MPI_INCON
Strict MPI summaries and reports require identical timer descriptor trees across
all ranks in the captured communicator. Extra timers, missing timers, renamed
timers, or different parent/child structure cause FTIMER_ERR_MPI_INCON.
Fix by making every participating rank create the same timer tree, or switch to the sparse union API for rank-conditional work:
call ftimer_mpi_union_summary(summary, ierr=ierr)
call ftimer_write_mpi_union_summary_csv("ftimer-mpi-union.csv", ierr=ierr)
Sparse union entries report explicit participation counts. A missing rank is absent from that descriptor; it is not the same as a rank that participated with a real zero-time, zero-call entry.
MPI Summary Returns FTIMER_ERR_ACTIVE
MPI summaries and MPI report/CSV writers require a fully stopped timer set. If
any participating rank still has an active timer, the collective summary/report
path returns FTIMER_ERR_ACTIVE and leaves the MPI result or normal artifact
empty.
Stop every timer on every rank before calling:
call ftimer_stop("phase", ierr=ierr)
call ftimer_mpi_summary(summary, ierr=ierr)
For local debugging snapshots, use ftimer_get_summary() separately. Local
summary APIs can show active timers; MPI summary APIs intentionally do not.
MPI Summary Hangs
The practical failure mode for divergent communicators or missing collective participants is a hang. fTimer can detect descriptor disagreement only after all ranks have entered the same communicator collective. It cannot safely rendezvous across different communicator choices after the application has already split.
Check that:
- Every rank that belongs to the captured communicator reaches the same
mpi_summary, MPI report, or MPI CSV call. - All ranks captured the same communicator choice at
init. - Caller-owned subcommunicators were not freed before fTimer finished using them.
- Rank-conditional code does not skip the collective itself. Use sparse union APIs for conditional descriptors, not conditional collective entry.
MPI APIs Return FTIMER_ERR_NOT_IMPLEMENTED
If fTimer was built with FTIMER_USE_MPI=OFF, MPI summary/report APIs return
FTIMER_ERR_NOT_IMPLEMENTED. They do not fall back to local summaries and do
not create or replace MPI report files.
Reconfigure with MPI enabled if you need MPI reports:
FC=mpifort cmake -B build-mpi-smoke -DFTIMER_USE_MPI=ON -DFTIMER_BUILD_TESTS=OFF
If you only need rank-local data from a non-MPI build, call the local summary or CSV APIs instead.
CSV Columns Look Different Between Files
fTimer has several CSV schemas. They are separate on purpose:
- Local and strict MPI CSV use
format_version=2. - Sparse MPI union CSV uses
format_version=1andsummary_kind=mpi_union. - Local OpenMP, strict MPI+OpenMP, and sparse MPI+OpenMP CSV each use their own
format_version=1schema.
Small examples:
call ftimer_write_summary_csv("local.csv", ierr=ierr)
call ftimer_write_mpi_summary_csv("mpi-strict.csv", ierr=ierr)
call ftimer_write_mpi_union_summary_csv("mpi-union.csv", ierr=ierr)
Do not append sparse union rows to a local/strict MPI CSV file or the reverse. Those headers are not append-compatible.
CSV Append Returns FTIMER_ERR_IO
With append=.true., fTimer validates the existing non-empty file before adding
new records. It rejects the append with FTIMER_ERR_IO if the target has:
- a mismatched header or older schema
- rows with the wrong field count
- unrecognized
summary_kindorrecord_typecombinations - malformed CSV quoting
- a final record that is not newline-terminated
- a schema from a different report family, such as sparse union CSV versus local/strict MPI CSV
The existing file is left unchanged on validation failure. Start a new output file, append only to files produced by the same API/schema, or repair the existing file so the header, records, quoting, and final newline are valid.
CSV Counts Or Text Fields Look Surprising
Local call_count and strict MPI min_call_count/max_call_count are emitted
as signed 64-bit decimal text. Parse them as at least signed 64-bit integers.
Strict MPI avg_call_count remains a real-valued field.
Timer names and metadata key/value fields use standard CSV quoting. CSV output does not use the visible escaping used by human text reports, and it is not spreadsheet-formula-sanitized. Treat CSV as data from the generating program when opening it in spreadsheet software.
Reports Look Incomplete
Local, strict MPI, sparse MPI union, local OpenMP, strict MPI+OpenMP, and sparse MPI+OpenMP report APIs are distinct surfaces.
print_summary()andwrite_summary()are local snapshot reports.print_mpi_summary()andwrite_mpi_summary()are strict MPI reports.print_mpi_union_summary()andwrite_mpi_union_summary()are sparse MPI union reports.- The
ftimer_openmp_treport families are separate from the classic local and pure-MPI report families.
Strict MPI text reports are abbreviated human reports. The complete
machine-facing data is in ftimer_mpi_summary_t or the strict MPI CSV output.
For sparse/rank-conditional work, use ftimer_mpi_union_summary_t or sparse
union CSV so participation and missing-rank counts are explicit.
Active Local Summaries Are Snapshots
Local summaries can include active timers as a snapshot at summary time. That is
useful for diagnostics, but it is not a final stopped-run report. Stop all
timers first when you need a final local report, and check
summary%has_active_timers == .false..
MPI summaries, MPI reports, OpenMP worker summaries, and hybrid summaries are
stricter merge/report points. They require stopped timers or stopped timed
regions and return FTIMER_ERR_ACTIVE when that preflight fails.
Timings Do Not Include Device Or Synchronized MPI Time
fTimer records host wall-clock time between your start and stop calls. It
does not synchronize GPU/device queues, and it does not insert MPI barriers. Add
device synchronization or MPI synchronization in the application when that is
the quantity you intend to measure.