Skip to content

Commit d506b28

Browse files
Add trace events and remove Sonar (#24)
The Sonar integration keeps breaking because they automatically remove projects after a period of inactivity. It adds too little value for the effort required to keep it running, so I am removing it. Newer versions of Clang-Tidy approach Sonar in utility so we will be using only that for now. If the project sees active development in the future again, we may or may not reconsider this. This PR incorporates the changes from #20. --------- Co-authored-by: Scott Dixon <[email protected]>
1 parent 10e2754 commit d506b28

File tree

6 files changed

+84
-98
lines changed

6 files changed

+84
-98
lines changed

.github/workflows/main.yml

Lines changed: 2 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name: Main Workflow
22
on: [push, pull_request]
33
env:
4-
LLVM_VERSION: 15
4+
LLVM_VERSION: 19
55
jobs:
66
test:
77
if: github.event_name == 'push'
@@ -39,78 +39,12 @@ jobs:
3939
- working-directory: ${{github.workspace}}/build
4040
run: make test
4141

42-
sonarcloud:
43-
if: >
44-
github.event_name == 'pull_request' ||
45-
contains(github.ref, '/master') ||
46-
contains(github.ref, '/release') ||
47-
contains(github.event.head_commit.message, '#sonar')
48-
runs-on: ubuntu-latest
49-
env:
50-
SONAR_SCANNER_VERSION: 5.0.1.3006
51-
SONAR_SERVER_URL: "https://sonarcloud.io"
52-
BUILD_WRAPPER_OUT_DIR: build_wrapper_output_directory
53-
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
54-
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
55-
CC: gcc
56-
CXX: g++
57-
steps:
58-
- uses: actions/checkout@v4
59-
with:
60-
fetch-depth: 0
61-
- run: sudo apt install g++ g++-multilib gcc-multilib
62-
- uses: actions/setup-java@v4
63-
with:
64-
java-version: 17
65-
distribution: zulu
66-
- uses: actions/cache@v4
67-
with:
68-
path: ~/.sonar/cache
69-
key: ${{ runner.os }}-sonar
70-
restore-keys: ${{ runner.os }}-sonar
71-
- name: Download and set up sonar-scanner
72-
env:
73-
SONAR_SCANNER_DOWNLOAD_URL: https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-${{ env.SONAR_SCANNER_VERSION }}-linux.zip
74-
run: |
75-
mkdir -p $HOME/.sonar
76-
curl -sSLo $HOME/.sonar/sonar-scanner.zip ${{ env.SONAR_SCANNER_DOWNLOAD_URL }}
77-
unzip -o $HOME/.sonar/sonar-scanner.zip -d $HOME/.sonar/
78-
echo "$HOME/.sonar/sonar-scanner-${{ env.SONAR_SCANNER_VERSION }}-linux/bin" >> $GITHUB_PATH
79-
- name: Download and set up build-wrapper
80-
env:
81-
BUILD_WRAPPER_DOWNLOAD_URL: ${{ env.SONAR_SERVER_URL }}/static/cpp/build-wrapper-linux-x86.zip
82-
run: |
83-
curl -sSLo $HOME/.sonar/build-wrapper-linux-x86.zip ${{ env.BUILD_WRAPPER_DOWNLOAD_URL }}
84-
unzip -o $HOME/.sonar/build-wrapper-linux-x86.zip -d $HOME/.sonar/
85-
echo "$HOME/.sonar/build-wrapper-linux-x86" >> $GITHUB_PATH
86-
# Pass NDEBUG to exclude assert() from coverage; see https://github.com/pavel-kirienko/o1heap/issues/9
87-
- name: Run build-wrapper
88-
run: |
89-
cmake tests -DCMAKE_BUILD_TYPE=Debug -DNO_STATIC_ANALYSIS=1 -DCMAKE_C_FLAGS='-DNDEBUG=1'
90-
build-wrapper-linux-x86-64 --out-dir ${{ env.BUILD_WRAPPER_OUT_DIR }} make all
91-
make test
92-
gcov --preserve-paths --long-file-names $(find CMakeFiles/test_general_cov.dir -name '*.gcno')
93-
gcov --preserve-paths --long-file-names $(find CMakeFiles/test_private_cov.dir -name '*.gcno')
94-
# https://community.sonarsource.com/t/analyzing-a-header-only-c-library/51468
95-
- if: env.SONAR_TOKEN != ''
96-
run: >
97-
sonar-scanner
98-
--define sonar.projectKey="pavel-kirienko_o1heap"
99-
--define sonar.organization="pavel-kirienko"
100-
--define sonar.sources="o1heap/"
101-
--define sonar.sourceEncoding="UTF-8"
102-
--define sonar.cfamily.gcov.reportsPath="."
103-
--define sonar.cfamily.build-wrapper-output="${{ env.BUILD_WRAPPER_OUT_DIR }}"
104-
--define sonar.host.url="${{ env.SONAR_SERVER_URL }}"
105-
--define sonar.login=${{ secrets.SONAR_TOKEN }}
106-
$([ -z "$GITHUB_BASE_REF" ] && echo "--define sonar.branch.name=${GITHUB_REF##*/}" || true)
107-
10842
style_check:
10943
if: github.event_name == 'push'
11044
runs-on: ubuntu-latest
11145
steps:
11246
- uses: actions/checkout@v4
113-
- uses: DoozyX/clang-format-lint-action@v0.15
47+
- uses: DoozyX/clang-format-lint-action@v0.20
11448
with:
11549
source: './o1heap ./tests'
11650
exclude: './tests/catch'

README.md

Lines changed: 18 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
# O(1) heap
22

33
[![Main Workflow](https://github.com/pavel-kirienko/o1heap/actions/workflows/main.yml/badge.svg)](https://github.com/pavel-kirienko/o1heap/actions/workflows/main.yml)
4-
[![Reliability Rating](https://sonarcloud.io/api/project_badges/measure?project=pavel-kirienko_o1heap&metric=reliability_rating)](https://sonarcloud.io/dashboard?id=pavel-kirienko_o1heap)
5-
[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=pavel-kirienko_o1heap&metric=coverage)](https://sonarcloud.io/dashboard?id=pavel-kirienko_o1heap)
64

7-
O1heap is a highly deterministic constant-complexity memory allocator designed for
5+
O1Heap is a highly deterministic constant-complexity memory allocator designed for
86
hard real-time high-integrity embedded systems.
97
The name stands for *O(1) heap*.
108

@@ -45,10 +43,10 @@ This implementation derives from or is based on the ideas presented in:
4543
- "Worst case fragmentation of first fit and best fit storage allocation strategies" -- J. M. Robson, 1975.
4644

4745
This implementation does not make any non-trivial assumptions about the behavioral properties of the application.
48-
Per Herter [2014], it can be described as a *predictably bad allocator*:
46+
Per Herter \[2014], it can be described as a *predictably bad allocator*:
4947

5048
> An allocator that provably performs close to its worst-case memory behavior which, in turn,
51-
> is better than the worst-case behavior of the allocators discussed [in Herter 2014],
49+
> is better than the worst-case behavior of the allocators discussed \[in Herter 2014],
5250
> but much worse than the memory consumption of these for normal programs without (mostly theoretical)
5351
> bad (de-)allocation sequences.
5452
@@ -104,7 +102,7 @@ catastrophic fragmentation cannot occur.
104102

105103
The above-defined theoretical worst-case upper bound H may be prohibitively high for some
106104
memory-constrained applications.
107-
It has been shown [Robson 1975] that under a typical workload,
105+
It has been shown \[Robson 1975] that under a typical workload,
108106
for a sufficiently high amount of memory available to the allocator which is less than $H$,
109107
the probability of a (de-)allocation sequence that results in catastrophic fragmentation is low.
110108
When combined with an acceptable failure probability and a set of adequate assumptions about the behaviors of
@@ -113,7 +111,7 @@ the heap while ensuring a sufficient degree of predictability and reliability.
113111
The methods of such optimization are outside the scope of this document;
114112
interested readers are advised to consult with the referred publications.
115113

116-
Following some of the ideas from [Herter 2014], this implementation takes caching-related issues into consideration
114+
Following some of the ideas from \[Herter 2014], this implementation takes caching-related issues into consideration
117115
by choosing the most recently used memory fragments to minimize cache misses in the application.
118116

119117
### Implementation
@@ -238,6 +236,12 @@ If not overridden by the user, for some compilers `O1HEAP_CLZ(x)` will expand to
238236
For other compilers it will default to a slow software implementation,
239237
which is likely to significantly degrade the performance of the library.
240238

239+
#### O1HEAP_TRACE
240+
241+
This option is intended for advanced diagnostics and may be not useful in most applications.
242+
If defined and is nonzero, makes o1heap invoke `extern` trace functions (implemented in the application)
243+
on every allocation and deallocation. Please refer to `o1heap.h` for usage details.
244+
241245
## Development
242246

243247
### Dependencies
@@ -248,15 +252,6 @@ The following tools should be available locally to conduct library development:
248252
- An AMD64 machine.
249253
- (optional) Valgrind.
250254

251-
### Conventions
252-
253-
The codebase shall follow the [Zubax C/C++ Coding Conventions](https://kb.zubax.com/x/84Ah).
254-
Compliance is enforced through the following means:
255-
256-
- Clang-Tidy -- invoked automatically while building the test suite.
257-
- Clang-Format -- invoked manually as `make format`; enforced in CI/CD automatically.
258-
- SonarCloud -- invoked by CI/CD automatically.
259-
260255
### Testing
261256

262257
Please refer to the continuous integration configuration to see how to invoke the tests.
@@ -267,35 +262,33 @@ Update the version number macro in the header file and create a new git tag like
267262

268263
### MISRA compliance
269264

270-
MISRA compliance is enforced with the help of the following tools:
265+
MISRA compliance is enforced with the help of:
271266

272267
- Clang-Tidy -- invoked automatically during the normal build process.
273-
- SonarCloud -- invoked as part of the continuous integration build.
274268

275269
Every intentional deviation shall be documented and justified in-place using the following notation,
276270
followed by the appropriate static analyser warning suppression statement:
277271

278272
```c
279273
// Intentional violation of MISRA: <valid reason here>
280-
// NOSONAR
281-
// NOLINT
274+
// NOLINT(*-specific-rule)
282275
```
283276

284277
The list of intentional deviations can be obtained by simply searching the codebase for the above comments.
285278

286-
Do not suppress compliance warnings using the means provided by static analysis tools because such deviations
287-
are impossible to track at the source code level.
288-
An exception applies for the case of false-positive (invalid) warnings -- those should not be mentioned in the codebase.
289-
290279
## Further reading
291280

292281
- [Timing-Predictable Memory Allocation In Hard Real-Time Systems](https://publikationen.sulb.uni-saarland.de/bitstream/20.500.11880/26614/1/diss.pdf), J. Herter, 2014.
293282
- [Worst case fragmentation of first fit and best fit storage allocation strategies](https://academic.oup.com/comjnl/article/20/3/242/751782), J. M. Robson, 1975.
294283
- [Dynamic Memory Allocation In SQLite](https://sqlite.org/malloc.html) -- on Robson proof and deterministic fragmentation.
295-
- *[Russian]* [Динамическая память в системах жёсткого реального времени](https://habr.com/ru/post/486650/) -- issues with dynamic memory allocation in modern embedded RTOS and related popular misconceptions.
284+
- [Динамическая память в системах жёсткого реального времени](https://habr.com/ru/post/486650/) -- issues with dynamic memory allocation in modern embedded RTOS and related popular misconceptions.
296285

297286
## Changelog
298287

288+
### Next version WIP
289+
290+
- Add optional trace events, enabled via the `O1HEAP_TRACE` build-time option.
291+
299292
### v2.1
300293

301294
- Significantly accelerate (de-)allocation by replacing the naïve log2 implementation with fast CLZ intrinsics;

o1heap/o1heap.c

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,11 @@
1111
// OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
1212
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
1313
//
14-
// Copyright (c) 2020 Pavel Kirienko
14+
// Copyright (c) Pavel Kirienko
1515
// Authors: Pavel Kirienko <[email protected]>
1616

17+
// ReSharper disable CppDFANullDereference
18+
1719
#include "o1heap.h"
1820
#include <assert.h>
1921
#include <limits.h>
@@ -79,6 +81,20 @@ O1HEAP_PRIVATE uint_fast8_t O1HEAP_CLZ(const size_t x)
7981
}
8082
#endif
8183

84+
/// If O1HEAP_TRACE is defined and is nonzero, trace tools can get events when o1heap memory is allocated or freed.
85+
/// The corresponding events are delivered by invoking extern functions o1heapTraceAllocate() etc, defined in the
86+
/// application. Please refer to the documentation for those functions for the additional information.
87+
#ifndef O1HEAP_TRACE
88+
# define O1HEAP_TRACE 0
89+
#endif
90+
#if O1HEAP_TRACE
91+
# define O1HEAP_TRACE_ALLOCATE(handle, pointer, size) o1heapTraceAllocate(handle, pointer, size)
92+
# define O1HEAP_TRACE_FREE(handle, pointer, size) o1heapTraceFree(handle, pointer, size)
93+
#else
94+
# define O1HEAP_TRACE_ALLOCATE(handle, pointer, size) (void) 0
95+
# define O1HEAP_TRACE_FREE(handle, pointer, size) (void) 0
96+
#endif
97+
8298
// ---------------------------------------- INTERNAL DEFINITIONS ----------------------------------------
8399

84100
#if !defined(__STDC_VERSION__) || (__STDC_VERSION__ < 199901L)
@@ -365,8 +381,17 @@ void* o1heapAllocate(O1HeapInstance* const handle, const size_t amount)
365381
frag->header.used = true;
366382

367383
out = ((char*) frag) + O1HEAP_ALIGNMENT;
384+
O1HEAP_TRACE_ALLOCATE(handle, out, frag->header.size);
385+
}
386+
else
387+
{
388+
O1HEAP_TRACE_ALLOCATE(handle, out, amount);
368389
}
369390
}
391+
else
392+
{
393+
O1HEAP_TRACE_ALLOCATE(handle, out, amount);
394+
}
370395

371396
// Update the diagnostics.
372397
if (O1HEAP_LIKELY(handle->diagnostics.peak_request_size < amount))
@@ -401,6 +426,8 @@ void o1heapFree(O1HeapInstance* const handle, void* const pointer)
401426
O1HEAP_ASSERT(frag->header.size <= handle->diagnostics.capacity);
402427
O1HEAP_ASSERT((frag->header.size % FRAGMENT_SIZE_MIN) == 0U);
403428

429+
O1HEAP_TRACE_FREE(handle, pointer, frag->header.size);
430+
404431
// Even if we're going to drop the fragment later, mark it free anyway to prevent double-free.
405432
frag->header.used = false;
406433

o1heap/o1heap.h

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
// OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
1212
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
1313
//
14-
// Copyright (c) 2020 Pavel Kirienko
14+
// Copyright (c) Pavel Kirienko
1515
// Authors: Pavel Kirienko <[email protected]>
1616
//
1717
// READ THE DOCUMENTATION IN README.md.
@@ -116,6 +116,19 @@ bool o1heapDoInvariantsHold(const O1HeapInstance* const handle);
116116
/// If the handle pointer is NULL, the behavior is undefined.
117117
O1HeapDiagnostics o1heapGetDiagnostics(const O1HeapInstance* const handle);
118118

119+
/// Advanced diagnostic hooks; not used by default.
120+
/// If O1HEAP_TRACE is not defined or is zero, these functions should be left unimplemented.
121+
/// Iff O1HEAP_TRACE is defined and is nonzero, the library will emit trace events by invoking these functions,
122+
/// which are to be defined in the application (or linking will fail).
123+
///
124+
/// For allocations, note that if the allocated memory pointer is NULL, an allocation failure has occurred.
125+
/// In this case, the size reported is the number of bytes requested that the allocator could not provide.
126+
///
127+
/// When using the pointer provided via o1heapTraceFree(), the pointer memory must not be accessed.
128+
/// This pointer should only be used for its address.
129+
extern void o1heapTraceAllocate(O1HeapInstance* const handle, void* const allocated_memory, size_t size);
130+
extern void o1heapTraceFree(O1HeapInstance* const handle, void* const freed_memory, size_t size);
131+
119132
#ifdef __cplusplus
120133
}
121134
#endif

tests/CMakeLists.txt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,9 @@ endif ()
2525

2626
set(library_dir "${CMAKE_SOURCE_DIR}/../o1heap")
2727

28-
# Use -DNO_STATIC_ANALYSIS=1 to suppress static analysis.
29-
# If not suppressed, the tools used here shall be available, otherwise the build will fail.
30-
if (NOT NO_STATIC_ANALYSIS)
28+
# clang-tidy
29+
set(DISABLE_CLANG_TIDY OFF CACHE BOOL "Do not use Clang-Tidy")
30+
if (NOT DISABLE_CLANG_TIDY)
3131
# clang-tidy (separate config files per directory)
3232
find_program(clang_tidy NAMES clang-tidy)
3333
if (NOT clang_tidy)
@@ -99,3 +99,4 @@ gen_test_matrix(
9999
test_general.cpp
100100
""
101101
)
102+
gen_test("test_general_c11_x32_trc" "test_general.cpp" "O1HEAP_TRACE=1" c_std_11 "-m32" "-m32")

tests/test_general.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -603,3 +603,21 @@ TEST_CASE("General: invariant checker")
603603
dg.oom_count++;
604604
REQUIRE(heap->doInvariantsHold());
605605
}
606+
607+
extern "C" void o1heapTraceAllocate(O1HeapInstance* const handle, void* const allocated_memory, size_t size)
608+
{
609+
REQUIRE(handle != nullptr);
610+
const auto* const h = reinterpret_cast<internal::O1HeapInstance*>(handle);
611+
h->validate();
612+
(void) allocated_memory;
613+
(void) size;
614+
}
615+
616+
extern "C" void o1heapTraceFree(O1HeapInstance* const handle, void* const freed_memory, size_t size)
617+
{
618+
REQUIRE(handle != nullptr);
619+
const auto* const h = reinterpret_cast<internal::O1HeapInstance*>(handle);
620+
h->validate();
621+
(void) freed_memory;
622+
(void) size;
623+
}

0 commit comments

Comments
 (0)