xref: /haiku/src/tests/add-ons/kernel/debugger/c++filt.cpp (revision 13581b3d2a71545960b98fefebc5225b5bf29072)
1 /*
2  * Copyright 2018, Haiku, Inc. All rights reserved.
3  * Based on Demumble; Copyright 2016-2018, Nico Weber.
4  * 		https://github.com/nico/demumble/
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *   http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18 
19 #include <stddef.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <algorithm>
24 
25 #include "Demangler.h"
26 
27 
28 static void print_help(FILE* out)
29 {
30 	fprintf(out,
31 		"usage: haikuc++filt [options] [symbols...]\n"
32 		"\n"
33 		"if symbols are unspecified, reads from stdin.\n"
34 		"\n"
35 		"options:\n"
36 		"  -m         only print mangled names that were demangled,"
37 			"omit other output\n"
38 		"  -u         use unbuffered output\n"
39 		"  --no-gcc2	ignore GCC 2-style symbols\n");
40 }
41 
42 
43 static bool starts_with(const char* s, const char* prefix)
44 {
45 	return strncmp(s, prefix, strlen(prefix)) == 0;
46 }
47 
48 
49 static void print_demangled(const char* s)
50 {
51 	const char* cxa_in = s;
52 	if (starts_with(s, "__Z") || starts_with(s, "____Z"))
53 		cxa_in += 1;
54 	printf("%s", Demangler::Demangle(cxa_in).String());
55 }
56 
57 
58 static bool is_mangle_char_posix(char c)
59 {
60 	return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
61 		(c >= '0' && c <= '9') || c == '_';
62 }
63 
64 
65 static bool look_for_itanium_prefix(char** str, char* end)
66 {
67 	char* s = *str;
68 	s += strcspn(s, "_?");
69 	if (s == end)
70 		return false;
71 
72 	// Itanium symbols start with 1-4 underscores followed by Z.
73 	// strnstr() is BSD, so use a small local buffer and strstr().
74 	const int N = 5;  // == strlen("____Z")
75 	char prefix[N + 1];
76 	strncpy(prefix, s, N);
77 		prefix[N] = '\0';
78 	if (strstr(prefix, "_Z")) {
79 		*str = s;
80 		return true;
81 	}
82 	return false;
83 }
84 
85 
86 static bool look_for_gcc2_symbol(char** str, char* end)
87 {
88 	// search '__' starting from the end, don't accept them at the start
89 	char* s = *str;
90 	size_t pos = (end - s) - 1;
91 	char* mangled = NULL;
92 
93 	while (pos > 1) {
94 		if (s[pos] == '_') {
95 			if (s[pos - 1] == '_') {
96 				mangled = s + pos + 1;
97 				break;
98 			} else
99 				pos--;
100 		}
101 		pos--;
102 	}
103 
104 	// if we've found a symbol, go backwards to its beginning
105 	while (mangled != NULL && mangled > (s + 1)
106 			&& is_mangle_char_posix(mangled[-1])) {
107 		mangled--;
108 	}
109 
110 	if (mangled != NULL)
111 		*str = mangled;
112 
113 	return mangled != NULL;
114 }
115 
116 
117 static char buf[8192];
118 int main(int argc, char* argv[])
119 {
120 	enum { kPrintAll, kPrintMatching } print_mode = kPrintAll;
121 	bool noGCC2 = false;
122 
123 	while (argc > 1 && argv[1][0] == '-') {
124 		if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0) {
125 			print_help(stdout);
126 			return 0;
127 		} else if (strcmp(argv[1], "-m") == 0) {
128 			print_mode = kPrintMatching;
129 		} else if (strcmp(argv[1], "--no-gcc2") == 0) {
130 			noGCC2 = true;
131 		} else if (strcmp(argv[1], "-u") == 0) {
132 			setbuf(stdout, NULL);
133 		} else if (strcmp(argv[1], "--") == 0) {
134 			--argc;
135 			++argv;
136 			break;
137 		} else {
138 			fprintf(stderr, "c++filt: unrecognized option `%s'\n", argv[1]);
139 			print_help(stderr);
140 			return 1;
141 		}
142 		--argc;
143 		++argv;
144 	}
145 	for (int i = 1; i < argc; ++i) {
146 		print_demangled(argv[i]);
147 		printf("\n");
148 	}
149 	if (argc != 1)
150 		return 0;
151 	// Read stdin instead.
152 	// By default, don't demangle types.  Mangled function names are unlikely
153 	// to appear in text for since they start with _Z (or ___Z) or ?? / ?$ / ?@.
154 	// But type manglings can be regular words ("Pi" is "int*").
155 	// (For command-line args, do try to demangle types though.)
156 	while (fgets(buf, sizeof(buf), stdin)) {
157 		bool need_separator = false;
158 		char* cur = buf;
159 		char* end = cur + strlen(cur);
160 
161 		while (cur != end) {
162 			if (print_mode == kPrintMatching && need_separator)
163 				printf("\n");
164 			need_separator = false;
165 
166 			// Check if we have a symbol, and then for how long it is.
167 			size_t n_sym = 0;
168 			char* real_cur = cur;
169 			if (look_for_itanium_prefix(&real_cur, end) ||
170 					(!noGCC2 && look_for_gcc2_symbol(&real_cur, end))) {
171 				// Print all the stuff before the symbol.
172 				if (print_mode == kPrintAll)
173 					printf("%.*s", static_cast<int>(real_cur - cur), cur);
174 				cur = real_cur;
175 				while (cur + n_sym != end && is_mangle_char_posix(cur[n_sym]))
176 					++n_sym;
177 			} else {
178 				// No symbols found in this block; skip it.
179 				printf("%s", cur);
180 				cur = end;
181 				continue;
182 			}
183 			if (n_sym == 0) {
184 				++cur;
185 				continue;
186 			}
187 
188 			char tmp = cur[n_sym];
189 			cur[n_sym] = '\0';
190 			print_demangled(cur);
191 			need_separator = true;
192 			cur[n_sym] = tmp;
193 
194 			cur += n_sym;
195 		}
196 	}
197 }
198