Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file removed .gitmodules
Empty file.
13 changes: 10 additions & 3 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ if (ENABLE_COVERAGE)
add_compile_options("--coverage" "-Og" "-g")
add_link_options("--coverage")
elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options("-Og" "-g" "--coverage" "-fprofile-instr-generate" "-fcoverage-mapping")
add_link_options("-fprofile-instr-generate" "--coverage" "-fcoverage-mapping")
add_compile_options("-Og" "-g" "-fprofile-instr-generate" "-fcoverage-mapping")
add_link_options("-fprofile-instr-generate" "-fcoverage-mapping")
else()
message(WARNING "Code coverage not supported for this compiler.")
endif ()
Expand Down Expand Up @@ -63,6 +63,7 @@ add_library(Simo SHARED
src/module/core/CoreModules.cc
src/statistics/StatMapper.cc
src/core/InitializationStatus.cc
src/core/Log.cc
)

if ("${ENABLE_RELEASE_LTO}")
Expand Down Expand Up @@ -105,7 +106,7 @@ target_include_directories(Simo PRIVATE src)
add_executable(SimoSim src/SimoSim/SimoSim.cc)
target_link_libraries(SimoSim PRIVATE Simo)

install(TARGETS Simo)
install(TARGETS Simo SimoSim)
install(DIRECTORY include/
DESTINATION include
)
Expand Down Expand Up @@ -161,6 +162,12 @@ target_sources(test_Statistics PRIVATE
)
target_link_libraries(test_Statistics PRIVATE Simo)

create_unit_test_executable(test_Module)
target_sources(test_Module PRIVATE
tests/module/ModuleTest.cc
)
target_link_libraries(test_Module PRIVATE Simo)

add_library(SimoTestPingPongCollection SHARED
tests/collection/PingPongCollection.cc
)
Expand Down
24 changes: 15 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,23 +15,29 @@ it is missing: a simple, easy to use library that does what you need.

Guides (Markdown files inside `docs` folder) and Doxygen built from the main branch are stored in **[GitHub Pages](https://fusiled.github.io/Simo/)** .

## Get and initialize the repository

```bash
git clone https://github.com/fusiled/Simo
cd Simo
git submodule update --init
```

## Build

Clone Simo and initialize the submodules:

Build dependencies:
- CMake >= 3.31.0
- Boost ([Boost.Test](https://www.boost.org/libs/test),
[Boost.TypeIndex](https://www.boost.org/libs/type_index))
- Boost ([Boost.Test](https://www.boost.org/libs/test), [Boost.TypeIndex](https://www.boost.org/libs/type_index))
- [Glaze](https://github.com/stephenberry/glaze)
- [Doxygen](https://www.doxygen.nl/index.html) (for documentation)

Library usage dependencies (headers-only):
- Boost ([Boost.TypeIndex](https://www.boost.org/libs/type_index))
- [Glaze](https://github.com/stephenberry/glaze)

The build will produce `libSimo`.
The build will produce `libSimo` (core library) and `SimoSim` (a general purpose
executable to run simulations).

The [nix](https://nixos.org/learn/) flake simplifies the set up of the dependencies. With nix installed you can:
- Build the package with `nix build`
The [nix](https://nixos.org/learn/) flake simplifies the setup of the dependencies. With nix installed you can:
- Build the package with `nix build`. The build will be accessible in the `result` folder.
- Run `nix develop` to enter a virtual environment with all the required tools installed.
The GitHub actions run entering the virtual environment of `nix develop`.

Expand Down
105 changes: 105 additions & 0 deletions include/Simo/core/Log.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/*
* Copyright 2026 Matteo Fusi and Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#ifndef SIMO_LOG_HH
#define SIMO_LOG_HH
#include <Simo/compiler/Compiler.h>

#include <filesystem>
#include <format>
#include <fstream>
#include <vector>

#include "InitializationStatus.h"

namespace Simo::Log {

enum SIMO_PUBLIC LogLevel : std::uint8_t {
VERBOSE = 0,
DEBUG,
INFO,
WARNING,
};

class SIMO_PUBLIC Logger {
public:
/// Initialize the logger
InitializationStatus initialize(const std::filesystem::path& sink_path_v);

/// Add a log level and give it a name
Logger& add_log_level(size_t level, std::string_view name);

/// Setup default log levels and set default log level to the highest
Logger& populate_default_log_levels();

/// Setup log level explicitly
Logger& log_level(LogLevel level);
Logger& log_level(uint8_t level);

/// Setup log level from string. Do nothing is string is not recognized
Logger& log_level(std::string_view level_name);

size_t log_level() const;

/// Enable/disable logger
Logger& enabled(bool new_enabled_value);

bool enabled() const;

template <typename Callable>
Logger& log_callable(const size_t level, const bool print_level,
Callable&& c) {
if (!enabled() || level < log_level()) {
return *this;
}
if (print_level) {
*sink << '[' << level_map[level] << "] ";
}
*sink << std::format("{}", c());
return *this;
}

template <typename... Args>
Logger& log_format(const size_t level, const bool print_level, Args... args) {
if (!enabled() || level < log_level()) {
return *this;
}
if (print_level) {
*sink << '[' << level_map[level] << "] ";
}
*sink << std::format(std::forward<Args>(args)...);
return *this;
}

~Logger();

/// Flush sink at path if this path is identified as a used sink
static void flush_sink(const std::filesystem::path& path);

// Flush all the registered sinks
static void flush_all_sinks();

protected:
std::filesystem::path sink_path;
std::ofstream* sink = nullptr;
bool is_enabled = false;
size_t current_log_level = 0;
std::vector<std::string> level_map;
};

} // namespace Simo::Log

#endif // SIMO_LOG_HH
8 changes: 8 additions & 0 deletions include/Simo/core/Time.h
Original file line number Diff line number Diff line change
Expand Up @@ -171,4 +171,12 @@ struct to<Format, Time> {

} // namespace glz

template <>
struct std::formatter<Simo::Time> : std::formatter<std::string> {
auto format(Simo::Time t, format_context& ctx) const {
return formatter<string>::format(std::format("{} ps", t.to_picoseconds()),
ctx);
}
};

#endif // SIMO_TIME_HH
48 changes: 46 additions & 2 deletions include/Simo/module/Module.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
#ifndef SIMO_MODULE_HH
#define SIMO_MODULE_HH

#include <Simo/core/Log.h>
#include <Simo/core/Time.h>

#include <string>

#include "Simo/core/InitializationStatus.h"
Expand All @@ -33,7 +36,11 @@ class SIMO_PUBLIC Parameters {

[[nodiscard]] std::string_view name() const;

void name(std::string_view name);
template <typename Self>
Self& name(this Self& self, const std::string_view name) {
self.name_ = name;
return self;
}

// TODO make it to return a list of errors

Expand Down Expand Up @@ -94,13 +101,15 @@ class SIMO_PUBLIC Module {

[[nodiscard]] Context& sim_ctx() const;

virtual ~Module() = default;
virtual ~Module() {}

/// Record a statistic in a StatMapper to dump statistics
void record_statistics(Statistics::StatMapper& mapper);

[[nodiscard]] Port* get_port(std::string_view);

Time current_time() const;

template <typename Stat>
Stat* get_statistic(const std::string_view name) {
return statistics.get<Stat>(name);
Expand All @@ -111,6 +120,40 @@ class SIMO_PUBLIC Module {
statistics.visit(f);
}

/// Setup logging for the component
///
/// Enable log by default when the operation succeeds. Calling this function
/// will re-initialize the log even if the same out_file is passed
virtual InitializationStatus log_setup(
const std::filesystem::path& out_file = "simo.log");

/// Enable/disable logging for the component
void log_enable(bool new_value);

void log_level(size_t level);
void log_level(std::string_view level_name);

/// Log a message with the given level. The message is generated by the
/// callable. New-line is added
///
/// The callable is evaluated only when the log is enabled and the log level
/// is satisfied
template <typename Callable>
void log(size_t level, Callable&& callable, bool print_level = false) {
auto f = [this, callable]() {
return std::format("[{}] [{}] {}\n", current_time(), name(), callable());
};
log_raw_callable(level, print_level, f);
}

/// Log a message with the given level without adding timestamp
template <typename Callable>
void log_raw_callable(size_t level, bool print_level, Callable&& callable) {
logger.log_callable(level, print_level, std::forward<Callable>(callable));
}

Log::Logger& get_logger() { return logger; }

protected:
/// Create a new statistic of type T
template <typename T, typename... Args>
Expand All @@ -130,6 +173,7 @@ class SIMO_PUBLIC Module {

Statistics::StatStorage statistics;
std::unordered_map<std::string, std::unique_ptr<Port>> ports;
Log::Logger logger;

private:
std::string name_;
Expand Down
Loading