Skip to content
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

feat(jemalloc): support to dump stats #1133

Merged
merged 12 commits into from
Sep 8, 2022
84 changes: 84 additions & 0 deletions .github/workflows/lint_and_test_cpp.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,90 @@ jobs:
./scripts/config_hdfs.sh
./run.sh test --on_travis -m ${{ matrix.test_module }}

build_with_jemalloc:
name: Build with jemalloc
needs: cpp_clang_format_linter
runs-on: ubuntu-latest
container:
image: apache/pegasus:thirdparties-bin-test-jemallc-ubuntu1804-${{ github.base_ref }}
steps:
- uses: actions/checkout@v2
- name: Setup cache
uses: actions/cache@v3
with:
path: |
/github/home/.ccache
key: jemalloc_ccache
- uses: dorny/paths-filter@v2
id: changes
with:
filters: |
thirdparty:
- '.github/workflows/thirdparty-regular-push.yml'
- 'docker/thirdparties-src/**'
- 'docker/thirdparties-bin/**'
- 'thirdparty/**'
- name: Unpack prebuilt third-parties
if: steps.changes.outputs.thirdparty == 'false'
run: unzip /root/thirdparties-bin.zip -d ./thirdparty
- name: Rebuild third-parties
if: steps.changes.outputs.thirdparty == 'true'
working-directory: thirdparty
run: |
mkdir build
cmake -DCMAKE_BUILD_TYPE=Release -DROCKSDB_PORTABLE=ON -DUSE_JEMALLOC=ON -B build/
cmake --build build/ -j $(nproc)
- name: Compilation
run: |
ccache -p
ccache -z
./run.sh build --test --skip_thirdparty -j $(nproc) -t release --use_jemalloc
ccache -s
- name: Pack Server
run: ./run.sh pack_server -j
- name: Pack Tools
run: ./run.sh pack_tools -j
- name: Tar files
run: |
rm -rf thirdparty
tar -zcvhf release_jemalloc_builder.tar DSN_ROOT/ src/builder/bin src/builder/src/server/test/config.ini --exclude='*CMakeFiles*'
- name: Upload Artifact
uses: actions/upload-artifact@v3
with:
name: release_jemalloc_artifact_${{ github.sha }}
path: release_jemalloc_builder.tar

test_with_jemalloc:
name: Test with jemallc
strategy:
fail-fast: false
matrix:
test_module:
- dsn_utils_tests
needs: build_with_jemalloc
runs-on: ubuntu-latest
container:
image: apache/pegasus:thirdparties-bin-test-jemallc-ubuntu1804-${{ github.base_ref }}
options: --cap-add=SYS_PTRACE
steps:
- uses: actions/checkout@v2
- name: Unpack prebuilt third-parties
run: unzip /root/thirdparties-bin.zip -d ./thirdparty
- name: Download Artifact
uses: actions/download-artifact@v3
with:
name: release_jemalloc_artifact_${{ github.sha }}
path: .
- name: Tar files
run: |
tar -zxvf release_jemalloc_builder.tar
- name: Unit Testing
run: |
export LD_LIBRARY_PATH=`pwd`/thirdparty/output/lib:/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/server
ulimit -s unlimited
./scripts/config_hdfs.sh
./run.sh test --on_travis -m ${{ matrix.test_module }}

build_pegasus_on_macos:
name: macOS
needs: cpp_clang_format_linter
Expand Down
1 change: 1 addition & 0 deletions src/rdsn/include/dsn/utility/enum_helper.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@

#define ENUM_BEGIN(type, invalid_value) ENUM_BEGIN2(type, type, invalid_value)

#define ENUM_REG2(type, name) helper->register_enum(#name, type::name);
#define ENUM_REG(e) helper->register_enum(#e, e);

#define ENUM_END2(type, name) \
Expand Down
45 changes: 45 additions & 0 deletions src/rdsn/src/replica/replica_stub.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@
#include <dsn/dist/fmt_logging.h>
#ifdef DSN_ENABLE_GPERF
#include <gperftools/malloc_extension.h>
#elif defined(DSN_USE_JEMALLOC)
#include "utils/je_ctl.h"
#endif
#include <dsn/utility/fail_point.h>
#include <dsn/dist/remote_command.h>
Expand Down Expand Up @@ -106,6 +108,8 @@ replica_stub::replica_stub(replica_state_subscriber subscriber /*= nullptr*/,
_get_tcmalloc_status_command = nullptr;
_max_reserved_memory_percentage_command = nullptr;
_release_all_reserved_memory_command = nullptr;
#elif defined(DSN_USE_JEMALLOC)
_dump_jemalloc_stats_command = nullptr;
#endif
_replica_state_subscriber = subscriber;
_is_long_subscriber = is_long_subscriber;
Expand Down Expand Up @@ -2261,6 +2265,41 @@ void replica_stub::open_service()
register_ctrl_command();
}

#if !defined(DSN_ENABLE_GPERF) && defined(DSN_USE_JEMALLOC)
void replica_stub::register_jemalloc_ctrl_command()
{
_dump_jemalloc_stats_command = ::dsn::command_manager::instance().register_command(
{"replica.dump-jemalloc-stats"},
fmt::format("replica.dump-jemalloc-stats <{}> [buffer size]", kAllJeStatsTypesStr),
"dump stats of jemalloc",
[](const std::vector<std::string> &args) {
if (args.empty()) {
return std::string("invalid arguments");
}

auto type = enum_from_string(args[0].c_str(), je_stats_type::INVALID);
if (type == je_stats_type::INVALID) {
return std::string("invalid stats type");
}

std::string stats("\n");

if (args.size() == 1) {
dsn::je_dump_stats(type, stats);
return stats;
}

uint64_t buf_sz;
if (!dsn::buf2uint64(args[1], buf_sz)) {
return std::string("invalid buffer size");
}

dsn::je_dump_stats(type, static_cast<size_t>(buf_sz), stats);
return stats;
});
}
#endif

void replica_stub::register_ctrl_command()
{
/// In simple_kv test, three replica apps are created, which means that three replica_stubs are
Expand Down Expand Up @@ -2411,6 +2450,8 @@ void replica_stub::register_ctrl_command()
auto release_bytes = gc_tcmalloc_memory(true);
return "OK, release_bytes=" + std::to_string(release_bytes);
});
#elif defined(DSN_USE_JEMALLOC)
register_jemalloc_ctrl_command();
#endif
_max_concurrent_bulk_load_downloading_count_command =
dsn::command_manager::instance().register_command(
Expand Down Expand Up @@ -2573,6 +2614,8 @@ void replica_stub::close()
UNREGISTER_VALID_HANDLER(_get_tcmalloc_status_command);
UNREGISTER_VALID_HANDLER(_max_reserved_memory_percentage_command);
UNREGISTER_VALID_HANDLER(_release_all_reserved_memory_command);
#elif defined(DSN_USE_JEMALLOC)
UNREGISTER_VALID_HANDLER(_dump_jemalloc_stats_command);
#endif
UNREGISTER_VALID_HANDLER(_max_concurrent_bulk_load_downloading_count_command);

Expand All @@ -2588,6 +2631,8 @@ void replica_stub::close()
_get_tcmalloc_status_command = nullptr;
_max_reserved_memory_percentage_command = nullptr;
_release_all_reserved_memory_command = nullptr;
#elif defined(DSN_USE_JEMALLOC)
_dump_jemalloc_stats_command = nullptr;
#endif
_max_concurrent_bulk_load_downloading_count_command = nullptr;

Expand Down
4 changes: 4 additions & 0 deletions src/rdsn/src/replica/replica_stub.h
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,8 @@ class replica_stub : public serverlet<replica_stub>, public ref_counter
// Try to release tcmalloc memory back to operating system
// If release_all = true, it will release all reserved-not-used memory
uint64_t gc_tcmalloc_memory(bool release_all);
#elif defined(DSN_USE_JEMALLOC)
void register_jemalloc_ctrl_command();
#endif

private:
Expand Down Expand Up @@ -382,6 +384,8 @@ class replica_stub : public serverlet<replica_stub>, public ref_counter
dsn_handle_t _get_tcmalloc_status_command;
dsn_handle_t _max_reserved_memory_percentage_command;
dsn_handle_t _release_all_reserved_memory_command;
#elif defined(DSN_USE_JEMALLOC)
dsn_handle_t _dump_jemalloc_stats_command;
#endif
dsn_handle_t _max_concurrent_bulk_load_downloading_count_command;

Expand Down
107 changes: 107 additions & 0 deletions src/rdsn/src/utils/je_ctl.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you 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
//
// http://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.

#ifdef DSN_USE_JEMALLOC

#include "je_ctl.h"

#include <cstring>
#include <vector>

#include <boost/algorithm/string/join.hpp>
#include <jemalloc/jemalloc.h>

#include <dsn/c/api_utilities.h>
#include <dsn/dist/fmt_logging.h>

#define RETURN_ARRAY_ELEM_BY_ENUM_TYPE(type, array) \
do { \
const auto index = static_cast<size_t>(type); \
dcheck_lt(index, sizeof(array) / sizeof(array[0])); \
return array[index]; \
} while (0);

namespace dsn {

namespace {

void je_stats_cb(void *opaque, const char *str)
{
if (str == nullptr) {
return;
}

auto stats = reinterpret_cast<std::string *>(opaque);
auto avail_capacity = stats->capacity() - stats->size();
auto len = strlen(str);
if (len > avail_capacity) {
len = avail_capacity;
}

stats->append(str, len);
}

void je_dump_malloc_stats(const char *opts, size_t buf_sz, std::string &stats)
{
// Avoid malloc in callback.
stats.reserve(buf_sz);

malloc_stats_print(je_stats_cb, &stats, opts);
}

const char *je_stats_type_to_opts(je_stats_type type)
{
static const char *opts_map[] = {
"gmdablxe", "mdablxe", "gblxe", "",
};

RETURN_ARRAY_ELEM_BY_ENUM_TYPE(type, opts_map);
}

size_t je_stats_type_to_default_buf_sz(je_stats_type type)
{
static const size_t buf_sz_map[] = {
2 * 1024, 4 * 1024, 1024 * 1024, 2 * 1024 * 1024,
};

RETURN_ARRAY_ELEM_BY_ENUM_TYPE(type, buf_sz_map);
}

} // anonymous namespace

std::string get_all_je_stats_types_str()
{
std::vector<std::string> names;
for (size_t i = 0; i < static_cast<size_t>(je_stats_type::COUNT); ++i) {
names.emplace_back(enum_to_string(static_cast<je_stats_type>(i)));
}
return boost::join(names, " | ");
}

void je_dump_stats(je_stats_type type, size_t buf_sz, std::string &stats)
{
je_dump_malloc_stats(je_stats_type_to_opts(type), buf_sz, stats);
}

void je_dump_stats(je_stats_type type, std::string &stats)
{
je_dump_stats(type, je_stats_type_to_default_buf_sz(type), stats);
}

} // namespace dsn

#endif // DSN_USE_JEMALLOC
67 changes: 67 additions & 0 deletions src/rdsn/src/utils/je_ctl.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you 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
//
// http://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.

#pragma once

#ifdef DSN_USE_JEMALLOC

#include <string>

#include <dsn/utility/enum_helper.h>

namespace dsn {

// `je_stats_type` defines the types of stats that are dumped for jemalloc allocator:
// * By SUMMARY_STATS, it will dump the briefest message of stats, which only referred the
// summary information;
// * By CONFIGS, it will dump the configurations of jemalloc;
// * By BRIEF_ARENA_STATS, it will dump necessary information about overall stats for all
// arenas and individual stats for each arena;
// * By DETAILED_STATS, it will dump the detailed stats for all arenas, bins, extents, etc.
enum class je_stats_type : size_t
{
SUMMARY_STATS,
CONFIGS,
BRIEF_ARENA_STATS,
DETAILED_STATS,
COUNT,
INVALID,
};

ENUM_BEGIN(je_stats_type, je_stats_type::INVALID)
ENUM_REG2(je_stats_type, SUMMARY_STATS)
ENUM_REG2(je_stats_type, CONFIGS)
ENUM_REG2(je_stats_type, BRIEF_ARENA_STATS)
ENUM_REG2(je_stats_type, DETAILED_STATS)
ENUM_END(je_stats_type)

std::string get_all_je_stats_types_str();
const std::string kAllJeStatsTypesStr = get_all_je_stats_types_str();

// Dump the stats of specified type to a string, with specified buffer size.
//
// The buffer is used to read stats from jemalloc allocator. The reason why an extra buffer is
// involved is that the stats can only be accessed in the callback function for jemalloc where
// memory allocation should be avoided.
void je_dump_stats(je_stats_type type, size_t buf_sz, std::string &stats);

// Dump the stats of specified type to a string, with default buffer size.
void je_dump_stats(je_stats_type type, std::string &stats);

} // namespace dsn

#endif // DSN_USE_JEMALLOC
Loading