Skip to content

Commit

Permalink
Store allocation/deallocation state for structured logging on sized m…
Browse files Browse the repository at this point in the history
…ismatch.

PiperOrigin-RevId: 703544971
Change-Id: Icc77bd43102a353d65e865afda1db4fa1153400b
  • Loading branch information
ckennelly authored and copybara-github committed Dec 6, 2024
1 parent 0a0b799 commit af4b961
Show file tree
Hide file tree
Showing 9 changed files with 237 additions and 8 deletions.
2 changes: 2 additions & 0 deletions tcmalloc/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ tcmalloc_deps = [
"@com_google_absl//absl/status",
"@com_google_absl//absl/status:statusor",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/strings:str_format",
"@com_google_absl//absl/numeric:bits",
"//tcmalloc/internal:config",
"//tcmalloc/internal:declarations",
Expand Down Expand Up @@ -297,6 +298,7 @@ create_tcmalloc_libraries(
"//tcmalloc/internal:logging",
"//tcmalloc/internal:memory_stats",
"//tcmalloc/internal:mincore",
"//tcmalloc/internal:mismatched_delete_state",
"//tcmalloc/internal:numa",
"//tcmalloc/internal:optimization",
"//tcmalloc/internal:page_size",
Expand Down
21 changes: 17 additions & 4 deletions tcmalloc/allocation_sampling.cc
Original file line number Diff line number Diff line change
Expand Up @@ -269,25 +269,35 @@ sized_ptr_t SampleifyAllocation(Static& state, size_t requested_size,
}

ABSL_ATTRIBUTE_NOINLINE
static void ReportMismatchedDelete(const SampledAllocation& alloc, size_t size,
static void ReportMismatchedDelete(Static& state,
const SampledAllocation& alloc, size_t size,
size_t requested_size,
std::optional<size_t> allocated_size) {
TC_LOG("*** GWP-ASan (https://google.github.io/tcmalloc/gwp-asan.html) has detected a memory error ***");
TC_LOG("Error originates from memory allocated at:");
PrintStackTrace(alloc.sampled_stack.stack, alloc.sampled_stack.depth);

size_t maximum_size;
if (allocated_size.value_or(requested_size) != requested_size) {
TC_LOG("Mismatched-size-delete of %v bytes (expected %v - %v bytes) at:",
size, requested_size, *allocated_size);

maximum_size = *allocated_size;
} else {
TC_LOG("Mismatched-size-delete of %v bytes (expected %v bytes) at:", size,
requested_size);

maximum_size = requested_size;
}
static void* stack[kMaxStackDepth];
const size_t depth = absl::GetStackTrace(stack, kMaxStackDepth, 1);
PrintStackTrace(stack, depth);

RecordCrash("GWP-ASan", "mismatched-size-delete");
state.mismatched_delete_state().Record(
size, requested_size, maximum_size,
absl::MakeSpan(alloc.sampled_stack.stack, alloc.sampled_stack.depth),
absl::MakeSpan(stack, depth));
abort();
}

Expand Down Expand Up @@ -325,6 +335,9 @@ static void ReportMismatchedDelete(Static& state, void* ptr, size_t size,
PrintStackTrace(stack, depth);

RecordCrash("GWP-ASan", "mismatched-size-delete");
state.mismatched_delete_state().Record(size, minimum_size, maximum_size,
std::nullopt,
absl::MakeSpan(stack, depth));
abort();
}

Expand Down Expand Up @@ -362,11 +375,11 @@ void MaybeUnsampleAllocation(Static& state, void* ptr,
if (sampled_allocation->sampled_stack.requested_size_returning) {
if (ABSL_PREDICT_FALSE(
!(requested_size <= *size && *size <= allocated_size))) {
ReportMismatchedDelete(*sampled_allocation, *size, requested_size,
allocated_size);
ReportMismatchedDelete(state, *sampled_allocation, *size,
requested_size, allocated_size);
}
} else if (ABSL_PREDICT_FALSE(size != requested_size)) {
ReportMismatchedDelete(*sampled_allocation, *size, requested_size,
ReportMismatchedDelete(state, *sampled_allocation, *size, requested_size,
std::nullopt);
}
}
Expand Down
14 changes: 14 additions & 0 deletions tcmalloc/internal/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,20 @@ cc_library(
],
)

cc_library(
name = "mismatched_delete_state",
hdrs = ["mismatched_delete_state.h"],
copts = TCMALLOC_DEFAULT_COPTS,
visibility = [
"//tcmalloc:__subpackages__",
],
deps = [
":config",
":logging",
"@com_google_absl//absl/types:span",
],
)

cc_library(
name = "residency",
srcs = ["residency.cc"],
Expand Down
107 changes: 107 additions & 0 deletions tcmalloc/internal/mismatched_delete_state.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
// Copyright 2024 The TCMalloc Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef TCMALLOC_MISMATCHED_DELETE_STATE_H_
#define TCMALLOC_MISMATCHED_DELETE_STATE_H_

#include <algorithm>
#include <cstddef>
#include <cstring>
#include <optional>

#include "absl/types/span.h"
#include "tcmalloc/internal/config.h"
#include "tcmalloc/internal/logging.h"

GOOGLE_MALLOC_SECTION_BEGIN
namespace tcmalloc {
namespace tcmalloc_internal {

class MismatchedDeleteState {
public:
constexpr MismatchedDeleteState() = default;

bool triggered() const { return triggered_; }

std::optional<absl::Span<void* const>> AllocationStack() const {
TC_ASSERT(triggered_);

if (!allocation_stack_depth_.has_value()) {
return std::nullopt;
}

return absl::MakeSpan(allocation_stack_, *allocation_stack_depth_);
}

absl::Span<void* const> DeallocationStack() const {
TC_ASSERT(triggered_);
TC_ASSERT(deallocation_stack_depth_.has_value());

return absl::MakeSpan(deallocation_stack_, *deallocation_stack_depth_);
}

size_t provided_size() const {
TC_ASSERT(triggered_);
return provided_;
}

size_t minimum_size() const {
TC_ASSERT(triggered_);
return minimum_;
}

size_t maximum_size() const {
TC_ASSERT(triggered_);
return maximum_;
}

void Record(size_t provided, size_t minimum, size_t maximum,
std::optional<absl::Span<void* const>> allocation_stack,
absl::Span<void* const> deallocation_stack) {
triggered_ = true;

provided_ = provided;
minimum_ = minimum;
maximum_ = maximum;

if (allocation_stack.has_value()) {
size_t allocation_stack_depth =
std::min<size_t>(kMaxStackDepth, allocation_stack->size());
memcpy(allocation_stack_, allocation_stack->data(),
sizeof(void*) * allocation_stack_depth);
allocation_stack_depth_ = allocation_stack_depth;
}

size_t deallocation_stack_depth =
std::min<size_t>(kMaxStackDepth, deallocation_stack.size());
memcpy(deallocation_stack_, deallocation_stack.data(),
sizeof(void*) * deallocation_stack_depth);
deallocation_stack_depth_ = deallocation_stack_depth;
}

private:
bool triggered_ = false;
size_t provided_ = 0, minimum_ = 0, maximum_ = 0;

void* allocation_stack_[kMaxStackDepth] = {};
std::optional<size_t> allocation_stack_depth_ = std::nullopt;
void* deallocation_stack_[kMaxStackDepth] = {};
std::optional<size_t> deallocation_stack_depth_ = std::nullopt;
};

} // namespace tcmalloc_internal
} // namespace tcmalloc
GOOGLE_MALLOC_SECTION_END

#endif // TCMALLOC_MISMATCHED_DELETE_STATE_H_
5 changes: 4 additions & 1 deletion tcmalloc/static_vars.cc
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
#include "tcmalloc/internal/explicitly_constructed.h"
#include "tcmalloc/internal/logging.h"
#include "tcmalloc/internal/mincore.h"
#include "tcmalloc/internal/mismatched_delete_state.h"
#include "tcmalloc/internal/numa.h"
#include "tcmalloc/internal/parameter_accessors.h"
#include "tcmalloc/internal/percpu.h"
Expand Down Expand Up @@ -125,6 +126,8 @@ ABSL_CONST_INIT PageMap Static::pagemap_;
ABSL_CONST_INIT GuardedPageAllocator Static::guardedpage_allocator_;
ABSL_CONST_INIT NumaTopology<kNumaPartitions, kNumBaseClasses>
Static::numa_topology_;
ABSL_CONST_INIT MismatchedDeleteState Static::mismatched_delete_state_;

// LINT.ThenChange(:static_vars_size)

ABSL_CONST_INIT Static tc_globals;
Expand All @@ -150,7 +153,7 @@ size_t Static::metadata_bytes() {
sizeof(allocation_samples) + sizeof(deallocation_samples) +
sizeof(sampled_alloc_handle_generator) + sizeof(peak_heap_tracker_) +
sizeof(guardedpage_allocator_) + sizeof(numa_topology_) +
sizeof(CacheTopology::Instance());
sizeof(CacheTopology::Instance()) + sizeof(mismatched_delete_state_);
// LINT.ThenChange(:static_vars)

const size_t allocated = arena().stats().bytes_allocated +
Expand Down
6 changes: 6 additions & 0 deletions tcmalloc/static_vars.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
#include "tcmalloc/internal/config.h"
#include "tcmalloc/internal/explicitly_constructed.h"
#include "tcmalloc/internal/logging.h"
#include "tcmalloc/internal/mismatched_delete_state.h"
#include "tcmalloc/internal/numa.h"
#include "tcmalloc/internal/sampled_allocation.h"
#include "tcmalloc/internal/sampled_allocation_recorder.h"
Expand Down Expand Up @@ -181,6 +182,10 @@ class Static final {
// structure, so figure out how much of it is actually resident.
static size_t pagemap_residence();

static MismatchedDeleteState& mismatched_delete_state() {
return mismatched_delete_state_;
}

static SizeClassConfiguration size_class_configuration();

private:
Expand Down Expand Up @@ -212,6 +217,7 @@ class Static final {
ABSL_CONST_INIT static PeakHeapTracker peak_heap_tracker_;
ABSL_CONST_INIT static NumaTopology<kNumaPartitions, kNumBaseClasses>
numa_topology_;
ABSL_CONST_INIT static MismatchedDeleteState mismatched_delete_state_;

// PageHeap uses a constructor for initialization. Like the members above,
// we can't depend on initialization order, so pageheap is new'd
Expand Down
1 change: 1 addition & 0 deletions tcmalloc/tcmalloc.cc
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_format.h"
#include "absl/strings/string_view.h"
#include "absl/time/clock.h"
#include "absl/types/span.h"
Expand Down
2 changes: 2 additions & 0 deletions tcmalloc/testing/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -414,10 +414,12 @@ create_tcmalloc_testsuite(
name = "memory_errors_test",
srcs = ["memory_errors_test.cc"],
copts = TCMALLOC_DEFAULT_COPTS,
shard_count = 3,
tags = ["nosan"],
deps = [
":testutil",
"//tcmalloc:malloc_extension",
"//tcmalloc/internal:config",
"//tcmalloc/internal:declarations",
"//tcmalloc/internal:logging",
"@com_github_google_benchmark//:benchmark",
Expand Down
Loading

0 comments on commit af4b961

Please sign in to comment.