1 /* $NetBSD: mem1.c,v 1.77 2023/12/03 18:17:41 rillig Exp $ */ 2 3 /* 4 * Copyright (c) 1994, 1995 Jochen Pohl 5 * All Rights Reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by Jochen Pohl for 18 * The NetBSD Project. 19 * 4. The name of the author may not be used to endorse or promote products 20 * derived from this software without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 24 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 25 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 27 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 31 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 */ 33 34 #if HAVE_NBTOOL_CONFIG_H 35 #include "nbtool_config.h" 36 #endif 37 38 #include <sys/cdefs.h> 39 #if defined(__RCSID) 40 __RCSID("$NetBSD: mem1.c,v 1.77 2023/12/03 18:17:41 rillig Exp $"); 41 #endif 42 43 #include <sys/param.h> 44 #include <stdlib.h> 45 #include <string.h> 46 47 #include "lint1.h" 48 49 /* 50 * Filenames allocated by record_filename are shared and have unlimited 51 * lifetime. 52 */ 53 struct filename { 54 const char *fn_name; 55 size_t fn_len; 56 int fn_id; 57 struct filename *fn_next; 58 }; 59 60 static struct filename *filenames; /* null-terminated array */ 61 static int next_filename_id; 62 63 /* Find the given filename, or return NULL. */ 64 static const struct filename * 65 search_filename(const char *s, size_t len) 66 { 67 const struct filename *fn; 68 69 for (fn = filenames; fn != NULL; fn = fn->fn_next) { 70 if (fn->fn_len == len && memcmp(fn->fn_name, s, len) == 0) 71 break; 72 } 73 return fn; 74 } 75 76 struct filename_replacement { 77 const char *orig; 78 size_t orig_len; 79 const char *repl; 80 const struct filename_replacement *next; 81 }; 82 83 static struct filename_replacement *filename_replacements; 84 85 void 86 add_directory_replacement(char *arg) 87 { 88 struct filename_replacement *r = xmalloc(sizeof(*r)); 89 90 char *sep = strchr(arg, '='); 91 if (sep == NULL) 92 err(1, "Bad replacement directory spec `%s'", arg); 93 *sep = '\0'; 94 95 r->orig = arg; 96 r->orig_len = (size_t)(sep - arg); 97 r->repl = sep + 1; 98 r->next = filename_replacements; 99 filename_replacements = r; 100 } 101 102 const char * 103 transform_filename(const char *name, size_t len) 104 { 105 static char buf[MAXPATHLEN]; 106 const struct filename_replacement *r; 107 108 for (r = filename_replacements; r != NULL; r = r->next) 109 if (r->orig_len < len && 110 memcmp(name, r->orig, r->orig_len) == 0) 111 break; 112 if (r == NULL) 113 return name; 114 (void)snprintf(buf, sizeof(buf), "%s%s", r->repl, name + r->orig_len); 115 return buf; 116 } 117 118 /* 119 * Return a copy of the filename s with unlimited lifetime. 120 * If the filename is new, write it to the output file. 121 */ 122 const char * 123 record_filename(const char *s, size_t slen) 124 { 125 126 const struct filename *existing_fn = search_filename(s, slen); 127 if (existing_fn != NULL) 128 return existing_fn->fn_name; 129 130 char *name = xmalloc(slen + 1); 131 (void)memcpy(name, s, slen); 132 name[slen] = '\0'; 133 134 struct filename *fn = xmalloc(sizeof(*fn)); 135 fn->fn_name = name; 136 fn->fn_len = slen; 137 fn->fn_id = next_filename_id++; 138 fn->fn_next = filenames; 139 filenames = fn; 140 141 outint(fn->fn_id); 142 outchar('s'); 143 outstrg(transform_filename(fn->fn_name, fn->fn_len)); 144 outchar('\n'); 145 146 return fn->fn_name; 147 } 148 149 /* Get the ID of a filename. */ 150 int 151 get_filename_id(const char *s) 152 { 153 const struct filename *fn; 154 155 if (s == NULL || (fn = search_filename(s, strlen(s))) == NULL) 156 return -1; 157 return fn->fn_id; 158 } 159 160 typedef struct memory_pools { 161 struct memory_pool *pools; 162 size_t cap; 163 } memory_pools; 164 165 /* Array of memory pools, indexed by mem_block_level. */ 166 static memory_pools mpools; 167 168 /* The pool for the current expression is independent of any block level. */ 169 static memory_pool expr_pool; 170 171 static void 172 mpool_add(memory_pool *pool, struct memory_pool_item item) 173 { 174 175 if (pool->len >= pool->cap) { 176 pool->cap = 2 * pool->len + 16; 177 pool->items = xrealloc(pool->items, 178 sizeof(*pool->items) * pool->cap); 179 } 180 pool->items[pool->len++] = item; 181 } 182 183 #ifdef DEBUG_MEM 184 static void 185 debug_memory_pool_item(const struct memory_pool_item *item) 186 { 187 void *p = item->p; 188 size_t size = item->size; 189 const char *descr = item->descr; 190 191 if (strcmp(descr, "string") == 0) { 192 const char *str = p; 193 debug_step("%s: freeing string '%s'", __func__, str); 194 } else if (strcmp(descr, "sym") == 0) { 195 const sym_t *sym = p; 196 debug_step("%s: freeing symbol '%s'", __func__, sym->s_name); 197 } else if (strcmp(descr, "type") == 0) { 198 const type_t *tp = p; 199 debug_step("%s: freeing type '%s'", __func__, type_name(tp)); 200 } else if (strcmp(descr, "tnode") == 0) { 201 const tnode_t *tn = p; 202 debug_step("%s: freeing node '%s' with type '%s'", 203 __func__, op_name(tn->tn_op), type_name(tn->tn_type)); 204 } else 205 debug_step("%s: freeing '%s' with %zu bytes", 206 __func__, descr, size); 207 } 208 #endif 209 210 static void 211 mpool_free(memory_pool *pool) 212 { 213 214 #ifdef DEBUG_MEM 215 for (size_t i = pool->len; i-- > 0; ) 216 debug_memory_pool_item(pool->items + i); 217 #endif 218 219 for (size_t i = pool->len; i-- > 0;) { 220 #ifdef DEBUG_MEM 221 static void *(*volatile set)(void *, int, size_t) = memset; 222 set(pool->items[i].p, 'Z', pool->items[i].size); 223 #endif 224 free(pool->items[i].p); 225 } 226 pool->len = 0; 227 } 228 229 static void * 230 #ifdef DEBUG_MEM 231 mpool_zero_alloc(memory_pool *pool, size_t size, const char *descr) 232 #else 233 mpool_zero_alloc(memory_pool *pool, size_t size) 234 #endif 235 { 236 237 void *mem = xmalloc(size); 238 memset(mem, 0, size); 239 #if DEBUG_MEM 240 mpool_add(pool, (struct memory_pool_item){ mem, size, descr }); 241 #else 242 mpool_add(pool, (struct memory_pool_item){ mem }); 243 #endif 244 return mem; 245 } 246 247 static memory_pool * 248 mpool_at(size_t level) 249 { 250 251 if (level >= mpools.cap) { 252 size_t prev_cap = mpools.cap; 253 mpools.cap = level + 16; 254 mpools.pools = xrealloc(mpools.pools, 255 sizeof(*mpools.pools) * mpools.cap); 256 for (size_t i = prev_cap; i < mpools.cap; i++) 257 mpools.pools[i] = (memory_pool){ NULL, 0, 0 }; 258 } 259 return mpools.pools + level; 260 } 261 262 263 /* Allocate memory associated with the level, initialized with zero. */ 264 #ifdef DEBUG_MEM 265 void * 266 level_zero_alloc(size_t level, size_t size, const char *descr) 267 { 268 269 debug_step("%s: %s at level %zu", __func__, descr, level); 270 return mpool_zero_alloc(mpool_at(level), size, descr); 271 } 272 #else 273 void * 274 (level_zero_alloc)(size_t level, size_t size) 275 { 276 277 return mpool_zero_alloc(mpool_at(level), size); 278 } 279 #endif 280 281 /* Allocate memory that is freed at the end of the current block. */ 282 #ifdef DEBUG_MEM 283 void * 284 block_zero_alloc(size_t size, const char *descr) 285 { 286 287 return level_zero_alloc(mem_block_level, size, descr); 288 } 289 #else 290 void * 291 (block_zero_alloc)(size_t size) 292 { 293 294 return (level_zero_alloc)(mem_block_level, size); 295 } 296 #endif 297 298 void 299 level_free_all(size_t level) 300 { 301 302 debug_step("+ %s %zu", __func__, level); 303 debug_indent_inc(); 304 mpool_free(mpool_at(level)); 305 debug_leave(); 306 } 307 308 /* Allocate memory that is freed at the end of the current expression. */ 309 #if DEBUG_MEM 310 void * 311 expr_zero_alloc(size_t s, const char *descr) 312 { 313 314 return mpool_zero_alloc(&expr_pool, s, descr); 315 } 316 #else 317 void * 318 (expr_zero_alloc)(size_t size) 319 { 320 321 return mpool_zero_alloc(&expr_pool, size); 322 } 323 #endif 324 325 static bool 326 str_ends_with(const char *haystack, const char *needle) 327 { 328 size_t hlen = strlen(haystack); 329 size_t nlen = strlen(needle); 330 331 return nlen <= hlen && 332 memcmp(haystack + hlen - nlen, needle, nlen) == 0; 333 } 334 335 /* 336 * Return a freshly allocated tree node that is freed at the end of the 337 * current expression. 338 * 339 * The node records whether it comes from a system file, which makes strict 340 * bool mode less restrictive. 341 */ 342 tnode_t * 343 expr_alloc_tnode(void) 344 { 345 tnode_t *tn = expr_zero_alloc(sizeof(*tn), "tnode"); 346 /* 347 * files named *.c that are different from the main translation unit 348 * typically contain generated code that cannot be influenced, such as 349 * a flex lexer or a yacc parser. 350 */ 351 tn->tn_sys = in_system_header || 352 (curr_pos.p_file != csrc_pos.p_file && 353 str_ends_with(curr_pos.p_file, ".c")); 354 return tn; 355 } 356 357 /* Free all memory which is allocated by the current expression. */ 358 void 359 expr_free_all(void) 360 { 361 362 debug_step("%s", __func__); 363 mpool_free(&expr_pool); 364 } 365 366 /* 367 * Save the memory which is used by the current expression. This memory 368 * is not freed by the next expr_free_all() call. The returned value can be 369 * used to restore the memory. 370 */ 371 memory_pool 372 expr_save_memory(void) 373 { 374 375 memory_pool saved_pool = expr_pool; 376 expr_pool = (memory_pool){ NULL, 0, 0 }; 377 return saved_pool; 378 } 379 380 /* 381 * Free all memory used for the current expression and restore the memory used 382 * by a previous expression and saved by expr_save_memory(). The next call to 383 * expr_free_all() frees the restored memory. 384 */ 385 void 386 expr_restore_memory(memory_pool saved_pool) 387 { 388 389 expr_free_all(); 390 free(expr_pool.items); 391 expr_pool = saved_pool; 392 } 393