706 lines · c
1// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.02/******************************************************************************3 *4 * Module Name: uttrack - Memory allocation tracking routines (debug only)5 *6 * Copyright (C) 2000 - 2023, Intel Corp.7 *8 *****************************************************************************/9 10/*11 * These procedures are used for tracking memory leaks in the subsystem, and12 * they get compiled out when the ACPI_DBG_TRACK_ALLOCATIONS is not set.13 *14 * Each memory allocation is tracked via a doubly linked list. Each15 * element contains the caller's component, module name, function name, and16 * line number. acpi_ut_allocate and acpi_ut_allocate_zeroed call17 * acpi_ut_track_allocation to add an element to the list; deletion18 * occurs in the body of acpi_ut_free.19 */20 21#include <acpi/acpi.h>22#include "accommon.h"23 24#ifdef ACPI_DBG_TRACK_ALLOCATIONS25 26#define _COMPONENT ACPI_UTILITIES27ACPI_MODULE_NAME("uttrack")28 29/* Local prototypes */30static struct acpi_debug_mem_block *acpi_ut_find_allocation(struct31 acpi_debug_mem_block32 *allocation);33 34static acpi_status35acpi_ut_track_allocation(struct acpi_debug_mem_block *address,36 acpi_size size,37 u8 alloc_type,38 u32 component, const char *module, u32 line);39 40static acpi_status41acpi_ut_remove_allocation(struct acpi_debug_mem_block *address,42 u32 component, const char *module, u32 line);43 44/*******************************************************************************45 *46 * FUNCTION: acpi_ut_create_list47 *48 * PARAMETERS: cache_name - Ascii name for the cache49 * object_size - Size of each cached object50 * return_cache - Where the new cache object is returned51 *52 * RETURN: Status53 *54 * DESCRIPTION: Create a local memory list for tracking purposed55 *56 ******************************************************************************/57 58acpi_status59acpi_ut_create_list(const char *list_name,60 u16 object_size, struct acpi_memory_list **return_cache)61{62 struct acpi_memory_list *cache;63 64 cache = acpi_os_allocate_zeroed(sizeof(struct acpi_memory_list));65 if (!cache) {66 return (AE_NO_MEMORY);67 }68 69 cache->list_name = list_name;70 cache->object_size = object_size;71 72 *return_cache = cache;73 return (AE_OK);74}75 76/*******************************************************************************77 *78 * FUNCTION: acpi_ut_allocate_and_track79 *80 * PARAMETERS: size - Size of the allocation81 * component - Component type of caller82 * module - Source file name of caller83 * line - Line number of caller84 *85 * RETURN: Address of the allocated memory on success, NULL on failure.86 *87 * DESCRIPTION: The subsystem's equivalent of malloc.88 *89 ******************************************************************************/90 91void *acpi_ut_allocate_and_track(acpi_size size,92 u32 component, const char *module, u32 line)93{94 struct acpi_debug_mem_block *allocation;95 acpi_status status;96 97 /* Check for an inadvertent size of zero bytes */98 99 if (!size) {100 ACPI_WARNING((module, line,101 "Attempt to allocate zero bytes, allocating 1 byte"));102 size = 1;103 }104 105 allocation =106 acpi_os_allocate(size + sizeof(struct acpi_debug_mem_header));107 if (!allocation) {108 109 /* Report allocation error */110 111 ACPI_WARNING((module, line,112 "Could not allocate size %u", (u32)size));113 114 return (NULL);115 }116 117 status =118 acpi_ut_track_allocation(allocation, size, ACPI_MEM_MALLOC,119 component, module, line);120 if (ACPI_FAILURE(status)) {121 acpi_os_free(allocation);122 return (NULL);123 }124 125 acpi_gbl_global_list->total_allocated++;126 acpi_gbl_global_list->total_size += (u32)size;127 acpi_gbl_global_list->current_total_size += (u32)size;128 129 if (acpi_gbl_global_list->current_total_size >130 acpi_gbl_global_list->max_occupied) {131 acpi_gbl_global_list->max_occupied =132 acpi_gbl_global_list->current_total_size;133 }134 135 return ((void *)&allocation->user_space);136}137 138/*******************************************************************************139 *140 * FUNCTION: acpi_ut_allocate_zeroed_and_track141 *142 * PARAMETERS: size - Size of the allocation143 * component - Component type of caller144 * module - Source file name of caller145 * line - Line number of caller146 *147 * RETURN: Address of the allocated memory on success, NULL on failure.148 *149 * DESCRIPTION: Subsystem equivalent of calloc.150 *151 ******************************************************************************/152 153void *acpi_ut_allocate_zeroed_and_track(acpi_size size,154 u32 component,155 const char *module, u32 line)156{157 struct acpi_debug_mem_block *allocation;158 acpi_status status;159 160 /* Check for an inadvertent size of zero bytes */161 162 if (!size) {163 ACPI_WARNING((module, line,164 "Attempt to allocate zero bytes, allocating 1 byte"));165 size = 1;166 }167 168 allocation =169 acpi_os_allocate_zeroed(size +170 sizeof(struct acpi_debug_mem_header));171 if (!allocation) {172 173 /* Report allocation error */174 175 ACPI_ERROR((module, line,176 "Could not allocate size %u", (u32)size));177 return (NULL);178 }179 180 status = acpi_ut_track_allocation(allocation, size,181 ACPI_MEM_CALLOC, component, module,182 line);183 if (ACPI_FAILURE(status)) {184 acpi_os_free(allocation);185 return (NULL);186 }187 188 acpi_gbl_global_list->total_allocated++;189 acpi_gbl_global_list->total_size += (u32)size;190 acpi_gbl_global_list->current_total_size += (u32)size;191 192 if (acpi_gbl_global_list->current_total_size >193 acpi_gbl_global_list->max_occupied) {194 acpi_gbl_global_list->max_occupied =195 acpi_gbl_global_list->current_total_size;196 }197 198 return ((void *)&allocation->user_space);199}200 201/*******************************************************************************202 *203 * FUNCTION: acpi_ut_free_and_track204 *205 * PARAMETERS: allocation - Address of the memory to deallocate206 * component - Component type of caller207 * module - Source file name of caller208 * line - Line number of caller209 *210 * RETURN: None211 *212 * DESCRIPTION: Frees the memory at Allocation213 *214 ******************************************************************************/215 216void217acpi_ut_free_and_track(void *allocation,218 u32 component, const char *module, u32 line)219{220 struct acpi_debug_mem_block *debug_block;221 acpi_status status;222 223 ACPI_FUNCTION_TRACE_PTR(ut_free, allocation);224 225 if (NULL == allocation) {226 ACPI_ERROR((module, line, "Attempt to delete a NULL address"));227 228 return_VOID;229 }230 231 debug_block = ACPI_CAST_PTR(struct acpi_debug_mem_block,232 (((char *)allocation) -233 sizeof(struct acpi_debug_mem_header)));234 235 acpi_gbl_global_list->total_freed++;236 acpi_gbl_global_list->current_total_size -= debug_block->size;237 238 status =239 acpi_ut_remove_allocation(debug_block, component, module, line);240 if (ACPI_FAILURE(status)) {241 ACPI_EXCEPTION((AE_INFO, status, "Could not free memory"));242 }243 244 acpi_os_free(debug_block);245 ACPI_DEBUG_PRINT((ACPI_DB_ALLOCATIONS, "%p freed (block %p)\n",246 allocation, debug_block));247 return_VOID;248}249 250/*******************************************************************************251 *252 * FUNCTION: acpi_ut_find_allocation253 *254 * PARAMETERS: allocation - Address of allocated memory255 *256 * RETURN: Three cases:257 * 1) List is empty, NULL is returned.258 * 2) Element was found. Returns Allocation parameter.259 * 3) Element was not found. Returns position where it should be260 * inserted into the list.261 *262 * DESCRIPTION: Searches for an element in the global allocation tracking list.263 * If the element is not found, returns the location within the264 * list where the element should be inserted.265 *266 * Note: The list is ordered by larger-to-smaller addresses.267 *268 * This global list is used to detect memory leaks in ACPICA as269 * well as other issues such as an attempt to release the same270 * internal object more than once. Although expensive as far271 * as cpu time, this list is much more helpful for finding these272 * types of issues than using memory leak detectors outside of273 * the ACPICA code.274 *275 ******************************************************************************/276 277static struct acpi_debug_mem_block *acpi_ut_find_allocation(struct278 acpi_debug_mem_block279 *allocation)280{281 struct acpi_debug_mem_block *element;282 283 element = acpi_gbl_global_list->list_head;284 if (!element) {285 return (NULL);286 }287 288 /*289 * Search for the address.290 *291 * Note: List is ordered by larger-to-smaller addresses, on the292 * assumption that a new allocation usually has a larger address293 * than previous allocations.294 */295 while (element > allocation) {296 297 /* Check for end-of-list */298 299 if (!element->next) {300 return (element);301 }302 303 element = element->next;304 }305 306 if (element == allocation) {307 return (element);308 }309 310 return (element->previous);311}312 313/*******************************************************************************314 *315 * FUNCTION: acpi_ut_track_allocation316 *317 * PARAMETERS: allocation - Address of allocated memory318 * size - Size of the allocation319 * alloc_type - MEM_MALLOC or MEM_CALLOC320 * component - Component type of caller321 * module - Source file name of caller322 * line - Line number of caller323 *324 * RETURN: Status325 *326 * DESCRIPTION: Inserts an element into the global allocation tracking list.327 *328 ******************************************************************************/329 330static acpi_status331acpi_ut_track_allocation(struct acpi_debug_mem_block *allocation,332 acpi_size size,333 u8 alloc_type,334 u32 component, const char *module, u32 line)335{336 struct acpi_memory_list *mem_list;337 struct acpi_debug_mem_block *element;338 acpi_status status = AE_OK;339 340 ACPI_FUNCTION_TRACE_PTR(ut_track_allocation, allocation);341 342 if (acpi_gbl_disable_mem_tracking) {343 return_ACPI_STATUS(AE_OK);344 }345 346 mem_list = acpi_gbl_global_list;347 status = acpi_ut_acquire_mutex(ACPI_MTX_MEMORY);348 if (ACPI_FAILURE(status)) {349 return_ACPI_STATUS(status);350 }351 352 /*353 * Search the global list for this address to make sure it is not354 * already present. This will catch several kinds of problems.355 */356 element = acpi_ut_find_allocation(allocation);357 if (element == allocation) {358 ACPI_ERROR((AE_INFO,359 "UtTrackAllocation: Allocation (%p) already present in global list!",360 allocation));361 goto unlock_and_exit;362 }363 364 /* Fill in the instance data */365 366 allocation->size = (u32)size;367 allocation->alloc_type = alloc_type;368 allocation->component = component;369 allocation->line = line;370 371 acpi_ut_safe_strncpy(allocation->module, (char *)module,372 ACPI_MAX_MODULE_NAME);373 374 if (!element) {375 376 /* Insert at list head */377 378 if (mem_list->list_head) {379 ((struct acpi_debug_mem_block *)(mem_list->list_head))->380 previous = allocation;381 }382 383 allocation->next = mem_list->list_head;384 allocation->previous = NULL;385 386 mem_list->list_head = allocation;387 } else {388 /* Insert after element */389 390 allocation->next = element->next;391 allocation->previous = element;392 393 if (element->next) {394 (element->next)->previous = allocation;395 }396 397 element->next = allocation;398 }399 400unlock_and_exit:401 status = acpi_ut_release_mutex(ACPI_MTX_MEMORY);402 return_ACPI_STATUS(status);403}404 405/*******************************************************************************406 *407 * FUNCTION: acpi_ut_remove_allocation408 *409 * PARAMETERS: allocation - Address of allocated memory410 * component - Component type of caller411 * module - Source file name of caller412 * line - Line number of caller413 *414 * RETURN: Status415 *416 * DESCRIPTION: Deletes an element from the global allocation tracking list.417 *418 ******************************************************************************/419 420static acpi_status421acpi_ut_remove_allocation(struct acpi_debug_mem_block *allocation,422 u32 component, const char *module, u32 line)423{424 struct acpi_memory_list *mem_list;425 acpi_status status;426 427 ACPI_FUNCTION_NAME(ut_remove_allocation);428 429 if (acpi_gbl_disable_mem_tracking) {430 return (AE_OK);431 }432 433 mem_list = acpi_gbl_global_list;434 if (NULL == mem_list->list_head) {435 436 /* No allocations! */437 438 ACPI_ERROR((module, line,439 "Empty allocation list, nothing to free!"));440 441 return (AE_OK);442 }443 444 status = acpi_ut_acquire_mutex(ACPI_MTX_MEMORY);445 if (ACPI_FAILURE(status)) {446 return (status);447 }448 449 /* Unlink */450 451 if (allocation->previous) {452 (allocation->previous)->next = allocation->next;453 } else {454 mem_list->list_head = allocation->next;455 }456 457 if (allocation->next) {458 (allocation->next)->previous = allocation->previous;459 }460 461 ACPI_DEBUG_PRINT((ACPI_DB_ALLOCATIONS, "Freeing %p, size 0%X\n",462 &allocation->user_space, allocation->size));463 464 /* Mark the segment as deleted */465 466 memset(&allocation->user_space, 0xEA, allocation->size);467 468 status = acpi_ut_release_mutex(ACPI_MTX_MEMORY);469 return (status);470}471 472/*******************************************************************************473 *474 * FUNCTION: acpi_ut_dump_allocation_info475 *476 * PARAMETERS: None477 *478 * RETURN: None479 *480 * DESCRIPTION: Print some info about the outstanding allocations.481 *482 ******************************************************************************/483 484void acpi_ut_dump_allocation_info(void)485{486/*487 struct acpi_memory_list *mem_list;488*/489 490 ACPI_FUNCTION_TRACE(ut_dump_allocation_info);491 492/*493 ACPI_DEBUG_PRINT (TRACE_ALLOCATIONS | TRACE_TABLES,494 ("%30s: %4d (%3d Kb)\n", "Current allocations",495 mem_list->current_count,496 ROUND_UP_TO_1K (mem_list->current_size)));497 498 ACPI_DEBUG_PRINT (TRACE_ALLOCATIONS | TRACE_TABLES,499 ("%30s: %4d (%3d Kb)\n", "Max concurrent allocations",500 mem_list->max_concurrent_count,501 ROUND_UP_TO_1K (mem_list->max_concurrent_size)));502 503 ACPI_DEBUG_PRINT (TRACE_ALLOCATIONS | TRACE_TABLES,504 ("%30s: %4d (%3d Kb)\n", "Total (all) internal objects",505 running_object_count,506 ROUND_UP_TO_1K (running_object_size)));507 508 ACPI_DEBUG_PRINT (TRACE_ALLOCATIONS | TRACE_TABLES,509 ("%30s: %4d (%3d Kb)\n", "Total (all) allocations",510 running_alloc_count,511 ROUND_UP_TO_1K (running_alloc_size)));512 513 ACPI_DEBUG_PRINT (TRACE_ALLOCATIONS | TRACE_TABLES,514 ("%30s: %4d (%3d Kb)\n", "Current Nodes",515 acpi_gbl_current_node_count,516 ROUND_UP_TO_1K (acpi_gbl_current_node_size)));517 518 ACPI_DEBUG_PRINT (TRACE_ALLOCATIONS | TRACE_TABLES,519 ("%30s: %4d (%3d Kb)\n", "Max Nodes",520 acpi_gbl_max_concurrent_node_count,521 ROUND_UP_TO_1K ((acpi_gbl_max_concurrent_node_count *522 sizeof (struct acpi_namespace_node)))));523*/524 return_VOID;525}526 527/*******************************************************************************528 *529 * FUNCTION: acpi_ut_dump_allocations530 *531 * PARAMETERS: component - Component(s) to dump info for.532 * module - Module to dump info for. NULL means all.533 *534 * RETURN: None535 *536 * DESCRIPTION: Print a list of all outstanding allocations.537 *538 ******************************************************************************/539 540void acpi_ut_dump_allocations(u32 component, const char *module)541{542 struct acpi_debug_mem_block *element;543 union acpi_descriptor *descriptor;544 u32 num_outstanding = 0;545 u8 descriptor_type;546 547 ACPI_FUNCTION_TRACE(ut_dump_allocations);548 549 if (acpi_gbl_disable_mem_tracking) {550 return_VOID;551 }552 553 /*554 * Walk the allocation list.555 */556 if (ACPI_FAILURE(acpi_ut_acquire_mutex(ACPI_MTX_MEMORY))) {557 return_VOID;558 }559 560 if (!acpi_gbl_global_list) {561 goto exit;562 }563 564 element = acpi_gbl_global_list->list_head;565 while (element) {566 if ((element->component & component) &&567 ((module == NULL)568 || (0 == strcmp(module, element->module)))) {569 descriptor =570 ACPI_CAST_PTR(union acpi_descriptor,571 &element->user_space);572 573 if (element->size <574 sizeof(struct acpi_common_descriptor)) {575 acpi_os_printf("%p Length 0x%04X %9.9s-%4.4u "576 "[Not a Descriptor - too small]\n",577 descriptor, element->size,578 element->module, element->line);579 } else {580 /* Ignore allocated objects that are in a cache */581 582 if (ACPI_GET_DESCRIPTOR_TYPE(descriptor) !=583 ACPI_DESC_TYPE_CACHED) {584 acpi_os_printf585 ("%p Length 0x%04X %9.9s-%4.4u [%s] ",586 descriptor, element->size,587 element->module, element->line,588 acpi_ut_get_descriptor_name589 (descriptor));590 591 /* Optional object hex dump */592 593 if (acpi_gbl_verbose_leak_dump) {594 acpi_os_printf("\n");595 acpi_ut_dump_buffer((u8 *)596 descriptor,597 element->598 size,599 DB_BYTE_DISPLAY,600 0);601 }602 603 /* Validate the descriptor type using Type field and length */604 605 descriptor_type = 0; /* Not a valid descriptor type */606 607 switch (ACPI_GET_DESCRIPTOR_TYPE608 (descriptor)) {609 case ACPI_DESC_TYPE_OPERAND:610 611 if (element->size ==612 sizeof(union613 acpi_operand_object))614 {615 descriptor_type =616 ACPI_DESC_TYPE_OPERAND;617 }618 break;619 620 case ACPI_DESC_TYPE_PARSER:621 622 if (element->size ==623 sizeof(union624 acpi_parse_object)) {625 descriptor_type =626 ACPI_DESC_TYPE_PARSER;627 }628 break;629 630 case ACPI_DESC_TYPE_NAMED:631 632 if (element->size ==633 sizeof(struct634 acpi_namespace_node))635 {636 descriptor_type =637 ACPI_DESC_TYPE_NAMED;638 }639 break;640 641 default:642 643 break;644 }645 646 /* Display additional info for the major descriptor types */647 648 switch (descriptor_type) {649 case ACPI_DESC_TYPE_OPERAND:650 651 acpi_os_printf652 ("%12.12s RefCount 0x%04X\n",653 acpi_ut_get_type_name654 (descriptor->object.common.655 type),656 descriptor->object.common.657 reference_count);658 break;659 660 case ACPI_DESC_TYPE_PARSER:661 662 acpi_os_printf663 ("AmlOpcode 0x%04X\n",664 descriptor->op.asl.665 aml_opcode);666 break;667 668 case ACPI_DESC_TYPE_NAMED:669 670 acpi_os_printf("%4.4s\n",671 acpi_ut_get_node_name672 (&descriptor->673 node));674 break;675 676 default:677 678 acpi_os_printf("\n");679 break;680 }681 }682 }683 684 num_outstanding++;685 }686 687 element = element->next;688 }689 690exit:691 (void)acpi_ut_release_mutex(ACPI_MTX_MEMORY);692 693 /* Print summary */694 695 if (!num_outstanding) {696 ACPI_INFO(("No outstanding allocations"));697 } else {698 ACPI_ERROR((AE_INFO, "%u (0x%X) Outstanding cache allocations",699 num_outstanding, num_outstanding));700 }701 702 return_VOID;703}704 705#endif /* ACPI_DBG_TRACK_ALLOCATIONS */706