-
Notifications
You must be signed in to change notification settings - Fork 4.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Refactor Stats::RawStatData into a StatsOptions struct #3629
Changes from 6 commits
bb349a1
7bfa762
27990bf
b92ab75
3a7aff1
3f38407
19f9f3c
06bd4c2
217f72f
1ae932c
b07d5c6
732af4f
7328dc0
71f6b24
2aa9d8c
911fc5d
dbcdb72
9a30244
aeb9251
cb0fd8a
19e0311
4cad816
9c6b880
c1fa6fb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -40,12 +40,12 @@ bool regexStartsWithDot(absl::string_view regex) { | |
// Normally the compiler would do this, but because name_ is a flexible-array-length | ||
// element, the compiler can't. RawStatData is put into an array in HotRestartImpl, so | ||
// it's important that each element starts on the required alignment for the type. | ||
uint64_t RawStatData::sizeGivenName(const absl::string_view name) { | ||
return roundUpMultipleNaturalAlignment(sizeof(RawStatData) + name.size() + 1); | ||
uint64_t RawStatData::structSize(uint64_t name_size) { | ||
return roundUpMultipleNaturalAlignment(sizeof(RawStatData) + name_size + 1); | ||
} | ||
|
||
uint64_t RawStatData::sizeGivenStatsOptions(const StatsOptions& stats_options) { | ||
return roundUpMultipleNaturalAlignment(sizeof(RawStatData) + stats_options.maxNameLength() + 1); | ||
uint64_t RawStatData::structSizeWithOptions(const StatsOptions& stats_options) { | ||
return structSize(stats_options.maxNameLength()); | ||
} | ||
|
||
std::string Utility::sanitizeStatsName(const std::string& name) { | ||
|
@@ -136,9 +136,12 @@ bool TagExtractorImpl::extractTag(const std::string& stat_name, std::vector<Tag> | |
} | ||
|
||
RawStatData* HeapRawStatDataAllocator::alloc(const std::string& name) { | ||
uint64_t num_bytes_to_allocate = RawStatData::sizeGivenName(name); | ||
uint64_t num_bytes_to_allocate = RawStatData::structSize(name.size()); | ||
RawStatData* data = static_cast<RawStatData*>(::calloc(num_bytes_to_allocate, 1)); | ||
data->initialize(name, num_bytes_to_allocate); | ||
if (data == nullptr) { | ||
throw EnvoyException("HeapRawStatDataAllocator: unable to allocate a new stat"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This might be caught. Can you throw some other type of exception? (Like whatever type new() throws). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I changed it to |
||
} | ||
data->checkAndInit(name, num_bytes_to_allocate); | ||
|
||
Thread::ReleasableLockGuard lock(mutex_); | ||
auto ret = stats_.insert(data); | ||
|
@@ -332,31 +335,31 @@ void HeapRawStatDataAllocator::free(RawStatData& data) { | |
::free(&data); | ||
} | ||
|
||
void RawStatData::initialize(absl::string_view key, uint64_t num_bytes_allocated) { | ||
void RawStatData::initialize(absl::string_view key, uint64_t xfer_size) { | ||
ASSERT(!initialized()); | ||
ref_count_ = 1; | ||
memcpy(name_, key.data(), xfer_size); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. so where is initialize called now? Does this need some safety guard for cope size? If not, can you add a comment? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So there are two initialize fns now -- one, There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we somehow assert that we aren't going to overwrite memory here? It's not super clear to me how we know that this is a safe copy (or if heap allocated pass in the size of the heap allocation and assert that)? |
||
name_[xfer_size] = '\0'; | ||
} | ||
|
||
void RawStatData::checkAndInit(absl::string_view key, uint64_t num_bytes_allocated) { | ||
uint64_t xfer_size = key.size(); | ||
ASSERT(xfer_size <= num_bytes_allocated); | ||
ASSERT(structSize(xfer_size) <= num_bytes_allocated); | ||
|
||
memcpy(name_, key.data(), xfer_size); | ||
name_[xfer_size] = '\0'; | ||
initialize(key, xfer_size); | ||
} | ||
|
||
void RawStatData::truncateAndInit(absl::string_view key, const StatsOptions& stats_options) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There may be a subtlety here that I'm missing here, but can we just truncate the string_view, and pass it to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I actually really like this. I will refactor |
||
ASSERT(!initialized()); | ||
if (key.size() > stats_options.maxNameLength()) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. maybe be more explicit here naming this function:
|
||
ENVOY_LOG_MISC( | ||
warn, | ||
"Statistic '{}' is too long with {} characters, it will be truncated to {} characters", key, | ||
key.size(), stats_options.maxNameLength()); | ||
} | ||
ref_count_ = 1; | ||
|
||
// key is not necessarily nul-terminated, but we want to make sure name_ is. | ||
uint64_t xfer_size = std::min(stats_options.maxNameLength(), key.size()); | ||
memcpy(name_, key.data(), xfer_size); | ||
name_[xfer_size] = '\0'; | ||
initialize(key, xfer_size); | ||
} | ||
|
||
HistogramStatisticsImpl::HistogramStatisticsImpl(const histogram_t* histogram_ptr) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -182,6 +182,11 @@ StatType& ThreadLocalStoreImpl::ScopeImpl::safeMakeStat( | |
make_stat(parent_.alloc_, name, std::move(tag_extracted_name), std::move(tags)); | ||
if (stat == nullptr) { | ||
parent_.num_last_resort_stats_.inc(); | ||
// TODO(jbuckland) Performing the fallback from non-heap allocator to heap allocator should be | ||
// the responsibility of the non-heap allocator, not the client of the non-heap allocator. | ||
// This branch will never be used in the non-hot-restart case, since the parent_.alloc_ object | ||
// will throw instead of returning a nullptr; we should remove the assumption that this | ||
// branching case is always available. | ||
stat = | ||
make_stat(parent_.heap_allocator_, name.substr(0, parent_.statsOptions().maxNameLength()), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This seems fishy. Since we now throw if the heap allocator fails to allocate, this statement will never be called in the non-hot restart case. However, it's still an embedded assumption about the specific behavior of all allocators - fixed length if they can fail and non-fixed length if they cannot fail. Is there any problem with changing the hot restart allocator to perform the fallback rather than relying on the caller? Since this is technically correct, I think it's fine to leave it this way for now, but please toss in a TODO to get rid of this assumption. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added this todo in cb0fd8a. |
||
std::move(tag_extracted_name), std::move(tags)); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know this wasn't being done before, but shouldn't we return early if
calloc
returns anullptr
so we don't segfault by trying to write to it? If we're not equipped to handle this failure at the callsite, then maybe we should just throw instead of returningnullptr
?