Skip to content

Commit dde6144

Browse files
AiyionPrimebingmann
andcommitted
stacktrace: add version 1.0.0
Co-authored-by: Timo Bingmann <[email protected]>
1 parent a786880 commit dde6144

File tree

6 files changed

+211
-0
lines changed

6 files changed

+211
-0
lines changed

recipes/stacktrace/all/conanfile.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
from conan import ConanFile
2+
from conan.errors import ConanInvalidConfiguration
3+
from conan.tools.build import check_min_cppstd
4+
from conan.tools.files import copy, get
5+
from conan.tools.layout import basic_layout
6+
import os
7+
8+
9+
required_conan_version = ">=2.0"
10+
11+
12+
class PackageConan(ConanFile):
13+
name = "stacktrace"
14+
version = "1.0"
15+
description = "Print stack backtrace programmatically with demangled function names"
16+
license = "WTFPL"
17+
url = "https://github.com/conan-io/conan-center-index"
18+
homepage = "https://panthema.net/2008/0901-stacktrace-demangled/"
19+
topics = ("stacktrace", "backtrace", "backtrace_symbols", "demangle", "header-only")
20+
package_type = "header-library"
21+
settings = "os", "arch", "compiler", "build_type"
22+
no_copy_source = True
23+
24+
def export_sources(self):
25+
copy(self, "stacktrace.h", self.recipe_folder, self.export_sources_folder)
26+
27+
def package_id(self):
28+
self.info.clear()
29+
30+
def validate(self):
31+
check_min_cppstd(self, 98)
32+
if str(self.settings.compiler) not in ["gcc", "clang"]:
33+
raise ConanInvalidConfiguration(f"{self.ref} does only demangle gcc and clang properly.")
34+
if self.settings.os == "Windows":
35+
raise ConanInvalidConfiguration(f"{self.ref} might not compile on Windows.")
36+
37+
def build(self):
38+
pass
39+
40+
def package(self):
41+
copy(self, "stacktrace.h", self.source_folder, os.path.join(self.package_folder, "include"))
42+
43+
def package_info(self):
44+
self.cpp_info.bindirs = []
45+
self.cpp_info.libdirs = []

recipes/stacktrace/all/stacktrace.h

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
// stacktrace.h (c) 2008, Timo Bingmann from http://idlebox.net/
2+
// published under the WTFPL v2.0
3+
4+
#ifndef _STACKTRACE_H_
5+
#define _STACKTRACE_H_
6+
7+
#include <stdio.h>
8+
#include <stdlib.h>
9+
#include <execinfo.h>
10+
#include <cxxabi.h>
11+
12+
/** Print a demangled stack backtrace of the caller function to FILE* out. */
13+
static inline void print_stacktrace(FILE *out = stderr, unsigned int max_frames = 63)
14+
{
15+
fprintf(out, "stack trace:\n");
16+
17+
// storage array for stack trace address data
18+
void* addrlist[max_frames+1];
19+
20+
// retrieve current stack addresses
21+
int addrlen = backtrace(addrlist, sizeof(addrlist) / sizeof(void*));
22+
23+
if (addrlen == 0) {
24+
fprintf(out, " <empty, possibly corrupt>\n");
25+
return;
26+
}
27+
28+
// resolve addresses into strings containing "filename(function+address)",
29+
// this array must be free()-ed
30+
char** symbollist = backtrace_symbols(addrlist, addrlen);
31+
32+
// allocate string which will be filled with the demangled function name
33+
size_t funcnamesize = 256;
34+
char* funcname = (char*)malloc(funcnamesize);
35+
36+
// iterate over the returned symbol lines. skip the first, it is the
37+
// address of this function.
38+
for (int i = 1; i < addrlen; i++)
39+
{
40+
char *begin_name = 0, *begin_offset = 0, *end_offset = 0;
41+
42+
// find parentheses and +address offset surrounding the mangled name:
43+
// ./module(function+0x15c) [0x8048a6d]
44+
for (char *p = symbollist[i]; *p; ++p)
45+
{
46+
if (*p == '(')
47+
begin_name = p;
48+
else if (*p == '+')
49+
begin_offset = p;
50+
else if (*p == ')' && begin_offset) {
51+
end_offset = p;
52+
break;
53+
}
54+
}
55+
56+
if (begin_name && begin_offset && end_offset
57+
&& begin_name < begin_offset)
58+
{
59+
*begin_name++ = '\0';
60+
*begin_offset++ = '\0';
61+
*end_offset = '\0';
62+
63+
// mangled name is now in [begin_name, begin_offset) and caller
64+
// offset in [begin_offset, end_offset). now apply
65+
// __cxa_demangle():
66+
67+
int status;
68+
char* ret = abi::__cxa_demangle(begin_name,
69+
funcname, &funcnamesize, &status);
70+
if (status == 0) {
71+
funcname = ret; // use possibly realloc()-ed string
72+
fprintf(out, " %s : %s+%s\n",
73+
symbollist[i], funcname, begin_offset);
74+
}
75+
else {
76+
// demangling failed. Output function name as a C function with
77+
// no arguments.
78+
fprintf(out, " %s : %s()+%s\n",
79+
symbollist[i], begin_name, begin_offset);
80+
}
81+
}
82+
else
83+
{
84+
// couldn't parse the line? print the whole line.
85+
fprintf(out, " %s\n", symbollist[i]);
86+
}
87+
}
88+
89+
free(funcname);
90+
free(symbollist);
91+
}
92+
93+
#endif // _STACKTRACE_H_
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
cmake_minimum_required(VERSION 3.15)
2+
project(test_package LANGUAGES CXX)
3+
4+
find_package(stacktrace REQUIRED CONFIG)
5+
6+
add_executable(${PROJECT_NAME} test_package.cpp)
7+
target_link_libraries(${PROJECT_NAME} PRIVATE stacktrace::stacktrace)
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
from conan import ConanFile
2+
from conan.tools.build import can_run
3+
from conan.tools.cmake import cmake_layout, CMake
4+
import os
5+
6+
7+
class TestPackageConan(ConanFile):
8+
settings = "os", "arch", "compiler", "build_type"
9+
generators = "CMakeDeps", "CMakeToolchain"
10+
11+
def layout(self):
12+
cmake_layout(self)
13+
14+
def requirements(self):
15+
self.requires(self.tested_reference_str)
16+
17+
def build(self):
18+
cmake = CMake(self)
19+
cmake.configure()
20+
cmake.build()
21+
22+
def test(self):
23+
if can_run(self):
24+
bin_path = os.path.join(self.cpp.build.bindir, "test_package")
25+
self.run(bin_path, env="conanrun")
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
#include "stacktrace.h"
2+
#include <map>
3+
4+
namespace Nu {
5+
6+
template<typename Type>
7+
struct Alpha {
8+
struct Beta {
9+
void func() {
10+
print_stacktrace();
11+
}
12+
void func(Type) {
13+
print_stacktrace();
14+
}
15+
};
16+
};
17+
18+
struct Gamma {
19+
template <int N>
20+
void unroll(double d) {
21+
unroll<N-1>(d);
22+
}
23+
};
24+
25+
template<>
26+
void Gamma::unroll<0>(double) {
27+
print_stacktrace();
28+
}
29+
30+
} // namespace Nu
31+
32+
int main(void) {
33+
Nu::Alpha<int>::Beta().func(42);
34+
Nu::Alpha<char*>::Beta().func("42");
35+
Nu::Alpha< Nu::Alpha< std::map<int, double> > >::Beta().func();
36+
Nu::Gamma().unroll<5>(42.0);
37+
return EXIT_SUCCESS;
38+
}

recipes/stacktrace/config.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
versions:
2+
"1.0.0":
3+
folder: all

0 commit comments

Comments
 (0)