PR changed-lines coverage: 13.50% (22/163) Uncovered changed code (with context): ================================================================================ src/Processors/Sources/JemallocProfileSource.cpp ================================================================================ --- uncovered block 64-65 --- 62 | 63 | src.remove_prefix(processed); >> 64 | return address; >> 65 | } 66 | 67 | /// Parse stack addresses from a jemalloc profile line starting with '@'. --- uncovered block 72-75 --- 70 | /// (they are return addresses, so we subtract 1 to point inside the call instruction). 71 | std::vector parseStackAddresses(std::string_view line) >> 72 | { >> 73 | std::vector result; >> 74 | if (line.empty() || line[0] != '@') >> 75 | return result; 76 | 77 | std::string_view sv(line.data() + 1, line.size() - 1); --- uncovered block 77-91 --- 75 | return result; 76 | >> 77 | std::string_view sv(line.data() + 1, line.size() - 1); >> 78 | bool first = true; >> 79 | while (!sv.empty()) >> 80 | { >> 81 | trimLeft(sv); >> 82 | if (sv.empty()) >> 83 | break; >> 84 | auto address = parseHexAddress(sv); >> 85 | if (!address.has_value()) >> 86 | break; >> 87 | result.push_back(first ? *address : *address - 1); >> 88 | first = false; >> 89 | } >> 90 | return result; >> 91 | } 92 | 93 | /// Simple LRU cache: evicts the least-recently-used entry when the capacity is exceeded. --- uncovered block 105-110 --- 103 | { 104 | size_t operator()(const Key & k) const >> 105 | { >> 106 | SipHash hash; >> 107 | hash.update(k.first); >> 108 | hash.update(k.second); >> 109 | return hash.get64(); >> 110 | } 111 | }; 112 | --- uncovered block 119-121 --- 117 | /// Returns the cached value on hit (moves the entry to the front), or nullopt on miss. 118 | std::optional get(const Key & key) >> 119 | { >> 120 | std::lock_guard lock(mutex); >> 121 | auto it = index.find(key); 122 | if (it == index.end()) 123 | return std::nullopt; --- uncovered block 125-126 --- 123 | return std::nullopt; 124 | lru.splice(lru.begin(), lru, it->second); >> 125 | return it->second->second; >> 126 | } 127 | 128 | /// Inserts a new entry (or updates existing) and returns the stored shared_ptr. --- uncovered block 130-149 --- 128 | /// Inserts a new entry (or updates existing) and returns the stored shared_ptr. 129 | Value put(const Key & key, std::vector value) >> 130 | { >> 131 | MemoryTrackerSwitcher switcher(&total_memory_tracker); >> 132 | auto shared = std::make_shared>(std::move(value)); >> 133 | std::lock_guard lock(mutex); >> 134 | auto it = index.find(key); >> 135 | if (it != index.end()) >> 136 | { >> 137 | it->second->second = shared; >> 138 | lru.splice(lru.begin(), lru, it->second); >> 139 | return shared; >> 140 | } >> 141 | lru.emplace_front(key, shared); >> 142 | index.emplace(key, lru.begin()); >> 143 | if (lru.size() > max_size) >> 144 | { >> 145 | index.erase(lru.back().first); >> 146 | lru.pop_back(); >> 147 | } >> 148 | return shared; >> 149 | } 150 | 151 | private: --- uncovered block 165-168 --- 163 | /// Callers reverse at output time as needed. 164 | SymbolizationLRUCache::Value resolveAddress(UInt64 address, bool symbolize_with_inline) >> 165 | { >> 166 | auto key = SymbolizationLRUCache::Key{address, symbolize_with_inline}; >> 167 | if (auto cached = symbolization_cache.get(key)) >> 168 | return *cached; 169 | 170 | FramePointers fp; --- uncovered block 170-179 --- 168 | return *cached; 169 | >> 170 | FramePointers fp; >> 171 | fp[0] = reinterpret_cast(address); >> 172 | std::vector frame_symbols; >> 173 | StackTrace::forEachFrame( >> 174 | fp, 0, 1, >> 175 | [&](const StackTrace::Frame & frame) >> 176 | { >> 177 | frame_symbols.push_back(frame.symbol.value_or("??")); >> 178 | }, >> 179 | symbolize_with_inline); 180 | 181 | return symbolization_cache.put(key, std::move(frame_symbols)); --- uncovered block 181-182 --- 179 | symbolize_with_inline); 180 | >> 181 | return symbolization_cache.put(key, std::move(frame_symbols)); >> 182 | } 183 | 184 | } --- uncovered block 300-300 --- 298 | } 299 | >> 300 | UInt64 address = addresses[current_address_index++]; 301 | 302 | auto symbols = resolveAddress(address, symbolize_with_inline); --- uncovered block 302-302 --- 300 | UInt64 address = addresses[current_address_index++]; 301 | >> 302 | auto symbols = resolveAddress(address, symbolize_with_inline); 303 | 304 | std::string symbol_line; --- uncovered block 304-306 --- 302 | auto symbols = resolveAddress(address, symbolize_with_inline); 303 | >> 304 | std::string symbol_line; >> 305 | WriteBufferFromString out(symbol_line); >> 306 | writePointerHex(reinterpret_cast(address), out); 307 | 308 | std::string_view separator(" "); --- uncovered block 308-312 --- 306 | writePointerHex(reinterpret_cast(address), out); 307 | >> 308 | std::string_view separator(" "); >> 309 | for (const auto & symbol : std::ranges::reverse_view(*symbols)) >> 310 | { >> 311 | writeString(separator, out); >> 312 | writeString(symbol, out); 313 | separator = std::string_view("--"); 314 | } --- uncovered block 351-352 --- 349 | } 350 | >> 351 | if (symbolized_phase == SymbolizedPhase::OutputtingHeap) >> 352 | { 353 | /// Re-read the profile file to stream heap lines (avoids storing all lines in memory) 354 | if (!file_input) --- uncovered block 354-355 --- 352 | { 353 | /// Re-read the profile file to stream heap lines (avoids storing all lines in memory) >> 354 | if (!file_input) >> 355 | file_input = std::make_unique(filename); 356 | 357 | while (!file_input->eof() && column->size() < max_block_size) --- uncovered block 357-363 --- 355 | file_input = std::make_unique(filename); 356 | >> 357 | while (!file_input->eof() && column->size() < max_block_size) >> 358 | { >> 359 | std::string line; >> 360 | readStringUntilNewlineInto(line, *file_input); >> 361 | file_input->tryIgnore(1); >> 362 | column->insertData(line.data(), line.size()); >> 363 | } 364 | 365 | if (file_input->eof()) --- uncovered block 365-368 --- 363 | } 364 | >> 365 | if (file_input->eof()) >> 366 | { >> 367 | symbolized_phase = SymbolizedPhase::Done; >> 368 | is_finished = true; 369 | } 370 | --- uncovered block 410-411 --- 408 | 409 | line.clear(); >> 410 | readStringUntilNewlineInto(line, in); >> 411 | in.tryIgnore(1); 412 | 413 | for (UInt64 addr : parseStackAddresses(line)) --- uncovered block 413-415 --- 411 | in.tryIgnore(1); 412 | >> 413 | for (UInt64 addr : parseStackAddresses(line)) >> 414 | unique_addresses.insert(addr); >> 415 | } 416 | 417 | /// Convert set to sorted vector for deterministic output --- uncovered block 418-420 --- 416 | 417 | /// Convert set to sorted vector for deterministic output >> 418 | addresses.assign(unique_addresses.begin(), unique_addresses.end()); >> 419 | std::sort(addresses.begin(), addresses.end()); >> 420 | } 421 | 422 | Chunk JemallocProfileSource::generateCollapsed() --- uncovered block 450-452 --- 448 | 449 | if (line[0] == '@') >> 450 | { >> 451 | current_stack = parseStackAddresses(line); >> 452 | } 453 | else if (!current_stack.empty() && line.contains(':')) 454 | { --- uncovered block 454-454 --- 452 | } 453 | else if (!current_stack.empty() && line.contains(':')) >> 454 | { 455 | /// Each allocation record follows its `@` stack line in the jemalloc heap profile format: 456 | /// --- uncovered block 488-489 --- 486 | UInt64 metric = parseInt(metric_str); 487 | >> 488 | if (metric > 0) >> 489 | { 490 | /// Build collapsed stack string: reverse stack to get root->leaf order, 491 | /// and for each address reverse the resolved frames (stored inline-first) --- uncovered block 493-498 --- 491 | /// and for each address reverse the resolved frames (stored inline-first) 492 | /// so that the main frame comes first within each address. >> 493 | std::string stack_str; >> 494 | WriteBufferFromString out(stack_str); >> 495 | bool first_symbol = true; >> 496 | for (UInt64 address : std::ranges::reverse_view(current_stack)) >> 497 | { >> 498 | if (isCancelled()) 499 | { 500 | is_finished = true; --- uncovered block 501-502 --- 499 | { 500 | is_finished = true; >> 501 | return {}; >> 502 | } 503 | 504 | auto symbols = resolveAddress(address, symbolize_with_inline); --- uncovered block 504-513 --- 502 | } 503 | >> 504 | auto symbols = resolveAddress(address, symbolize_with_inline); >> 505 | for (const auto & symbol : std::ranges::reverse_view(*symbols)) >> 506 | { >> 507 | if (!first_symbol) >> 508 | writeChar(';', out); >> 509 | first_symbol = false; >> 510 | writeString(symbol, out); >> 511 | } >> 512 | } >> 513 | out.finalize(); 514 | 515 | /// Aggregate metric for same stack --- uncovered block 516-518 --- 514 | 515 | /// Aggregate metric for same stack >> 516 | state.stack_to_metric[stack_str] += metric; >> 517 | } >> 518 | } 519 | 520 | current_stack.clear(); --- uncovered block 521-521 --- 519 | 520 | current_stack.clear(); >> 521 | } 522 | } 523 | --- uncovered block 537-537 --- 535 | } 536 | >> 537 | auto column = ColumnString::create(); 538 | 539 | for (size_t rows = 0; rows < max_block_size && state.iter != state.stack_to_metric.end(); ++rows, ++state.iter) --- uncovered block 539-543 --- 537 | auto column = ColumnString::create(); 538 | >> 539 | for (size_t rows = 0; rows < max_block_size && state.iter != state.stack_to_metric.end(); ++rows, ++state.iter) >> 540 | { >> 541 | auto formatted = fmt::format("{} {}", state.iter->first, state.iter->second); >> 542 | column->insertData(formatted.data(), formatted.size()); >> 543 | } 544 | 545 | if (state.iter == state.stack_to_metric.end()) --- uncovered block 545-549 --- 543 | } 544 | >> 545 | if (state.iter == state.stack_to_metric.end()) >> 546 | { >> 547 | is_finished = true; >> 548 | collapsed_state.reset(); >> 549 | } 550 | 551 | size_t num_rows = column->size(); --- uncovered block 551-551 --- 549 | } 550 | >> 551 | size_t num_rows = column->size(); 552 | Columns columns; 553 | columns.push_back(std::move(column)); WARNING: Failed to get start time for [Print Uncovered Code] - start time and duration won't be set