diff --git a/CMakeLists.txt b/CMakeLists.txt index 800bf47ca..47b5c9b53 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -67,6 +67,7 @@ option(NCNN_SIMPLEOCV "minimal opencv structure emulation" OFF) option(NCNN_SIMPLEOMP "minimal openmp runtime emulation" OFF) option(NCNN_SIMPLESTL "minimal cpp stl structure emulation" OFF) option(NCNN_SIMPLEMATH "minimal cmath" OFF) +option(NCNN_SIMPLEGCOV "minimal gcov emitting emulation" OFF) option(NCNN_THREADS "build with threads" ON) option(NCNN_BENCHMARK "print benchmark information for every layer" OFF) option(NCNN_C_API "build with C api" ON) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index cb7570a02..d491c6760 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -41,6 +41,7 @@ set(ncnn_SRCS simpleomp.cpp simplestl.cpp simplemath.cpp + simplegcov.cpp simplevk.cpp ) @@ -704,7 +705,11 @@ endif() if(NCNN_COVERAGE) target_compile_options(ncnn PUBLIC -coverage -fprofile-arcs -ftest-coverage) - target_link_libraries(ncnn PUBLIC -coverage -lgcov) + if(NCNN_SIMPLEGCOV) + target_link_libraries(ncnn PUBLIC -coverage) + else() + target_link_libraries(ncnn PUBLIC -coverage -lgcov) + endif() endif() if(NCNN_ASAN) @@ -753,6 +758,7 @@ if(NCNN_INSTALL_SDK) simpleomp.h simplestl.h simplemath.h + simplegcov.h simplevk.h vulkan_header_fix.h ${CMAKE_CURRENT_BINARY_DIR}/ncnn_export.h diff --git a/src/platform.h.in b/src/platform.h.in index 7bb27c6b0..6b91b454b 100644 --- a/src/platform.h.in +++ b/src/platform.h.in @@ -10,6 +10,7 @@ #cmakedefine01 NCNN_SIMPLEOMP #cmakedefine01 NCNN_SIMPLESTL #cmakedefine01 NCNN_SIMPLEMATH +#cmakedefine01 NCNN_SIMPLEGCOV #cmakedefine01 NCNN_THREADS #cmakedefine01 NCNN_BENCHMARK #cmakedefine01 NCNN_C_API diff --git a/src/simplegcov.cpp b/src/simplegcov.cpp new file mode 100644 index 000000000..bf6bafc99 --- /dev/null +++ b/src/simplegcov.cpp @@ -0,0 +1,179 @@ +// Tencent is pleased to support the open source community by making ncnn available. +// +// Copyright (C) 2025 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// 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. + +#include "platform.h" + +#if NCNN_SIMPLEGCOV + +#include "simplegcov.h" + +#include +#include +#include +#include + +struct gcda_function_arcs_info +{ + // function + uint32_t ident; + uint32_t func_checksum; + uint32_t cfg_checksum; + + // arcs + uint32_t num_counters; + uint64_t* counters; +}; + +struct gcda_info +{ + const char* orig_filename; + uint32_t version; + uint32_t checksum; + + std::vector fas; +}; + +static void write_gcda_files(); + +struct gdata +{ + std::vector gcdas; + + gdata() + { + atexit(write_gcda_files); + } +}; + +static struct gdata* g = 0; + +static void write_gcda_files() +{ + fprintf(stderr, "write_gcda_files\n"); + + // dump gcda + std::vector& gcdas = g->gcdas; + + for (size_t i = 0; i < gcdas.size(); i++) + { + const gcda_info& g = gcdas[i]; + + fprintf(stderr, "write %s\n", g.orig_filename); + + FILE* fp = fopen(g.orig_filename, "wb"); + if (!fp) + { + fprintf(stderr, "fopen %s failed %d\n", g.orig_filename, errno); + continue; + } + + const uint32_t GCOV_DATA_MAGIC = 0x67636461; + fwrite(&GCOV_DATA_MAGIC, 1, sizeof(uint32_t), fp); + fwrite(&g.version, 1, sizeof(uint32_t), fp); + fwrite(&g.checksum, 1, sizeof(uint32_t), fp); + + for (size_t j = 0; j < gcdas[i].fas.size(); j++) + { + const gcda_function_arcs_info& fa = gcdas[i].fas[j]; + + const uint32_t GCOV_TAG_FUNCTION = 0x01000000; + fwrite(&GCOV_TAG_FUNCTION, 1, sizeof(uint32_t), fp); + const uint32_t function_size = 3; + fwrite(&function_size, 1, sizeof(uint32_t), fp); + fwrite(&fa.ident, 1, sizeof(uint32_t), fp); + fwrite(&fa.func_checksum, 1, sizeof(uint32_t), fp); + fwrite(&fa.cfg_checksum, 1, sizeof(uint32_t), fp); + + const uint32_t GCOV_TAG_COUNTER_BASE = 0x01a10000; + fwrite(&GCOV_TAG_COUNTER_BASE, 1, sizeof(uint32_t), fp); + const uint32_t arcs_size = fa.num_counters * 2; + fwrite(&arcs_size, 1, sizeof(uint32_t), fp); + for (uint32_t k = 0; k < fa.num_counters; k++) + { + fwrite(&fa.counters[k], 1, sizeof(uint64_t), fp); + } + } + + fclose(fp); + } + + delete g; + g = 0; +} + +#ifdef __cplusplus +extern "C" { +#endif + +void llvm_gcov_init(llvm_gcov_callback writeout, llvm_gcov_callback flush) +{ + // fprintf(stderr, "llvm_gcov_init %p %p\n", writeout, flush); + + if (g == 0) + { + g = new gdata; + } + + writeout(); +} + +void llvm_gcda_start_file(const char* orig_filename, uint32_t version, uint32_t checksum) +{ + // fprintf(stderr, "llvm_gcda_start_file %s %u %u\n", orig_filename, version, checksum); + + std::vector& gcdas = g->gcdas; + + gcda_info g; + g.orig_filename = orig_filename; + g.version = version; + g.checksum = checksum; + gcdas.push_back(g); +} + +void llvm_gcda_emit_function(uint32_t ident, uint32_t func_checksum, uint32_t cfg_checksum) +{ + // fprintf(stderr, "llvm_gcda_emit_function %u %u %u\n", ident, func_checksum, cfg_checksum); + + std::vector& gcdas = g->gcdas; + + gcda_function_arcs_info fa = {ident, func_checksum, cfg_checksum, 0, 0}; + gcdas.back().fas.push_back(fa); +} + +void llvm_gcda_emit_arcs(uint32_t num_counters, uint64_t* counters) +{ + // fprintf(stderr, "llvm_gcda_emit_arcs %u %p\n", num_counters, counters); + + std::vector& gcdas = g->gcdas; + + gcda_function_arcs_info& fa = gcdas.back().fas.back(); + fa.num_counters = num_counters; + fa.counters = counters; +} + +void llvm_gcda_summary_info() +{ + // fprintf(stderr, "llvm_gcda_summary_info\n"); +} + +void llvm_gcda_end_file() +{ + // fprintf(stderr, "llvm_gcda_end_file\n"); +} + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // NCNN_SIMPLEGCOV diff --git a/src/simplegcov.h b/src/simplegcov.h new file mode 100644 index 000000000..bbb4150fe --- /dev/null +++ b/src/simplegcov.h @@ -0,0 +1,50 @@ +// Tencent is pleased to support the open source community by making ncnn available. +// +// Copyright (C) 2025 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// 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 NCNN_SIMPLEGCOV_H +#define NCNN_SIMPLEGCOV_H + +#include "platform.h" + +#if NCNN_SIMPLEGCOV + +#include + +// This minimal gcov emitting implementation only supports the llvm abi + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void (*llvm_gcov_callback)(); + +NCNN_EXPORT void llvm_gcov_init(llvm_gcov_callback writeout, llvm_gcov_callback flush); + +NCNN_EXPORT void llvm_gcda_start_file(const char* orig_filename, uint32_t version, uint32_t checksum); + +NCNN_EXPORT void llvm_gcda_emit_function(uint32_t ident, uint32_t func_checksum, uint32_t cfg_checksum); + +NCNN_EXPORT void llvm_gcda_emit_arcs(uint32_t num_counters, uint64_t* counters); + +NCNN_EXPORT void llvm_gcda_summary_info(); + +NCNN_EXPORT void llvm_gcda_end_file(); + +#ifdef __cplusplus +} +#endif + +#endif // NCNN_SIMPLEGCOV + +#endif // NCNN_SIMPLEGCOV_H