Skip to content

Commit

Permalink
[CELEBORN-1740][CIP-14] Add stackTrace utils to cppClient
Browse files Browse the repository at this point in the history
### What changes were proposed in this pull request?
This PR adds StackTrace utils code to CppClient.

### Why are the changes needed?
To provide StackTrace utils.

### Does this PR introduce _any_ user-facing change?
No.

### How was this patch tested?
Compilation.

Closes #2951 from HolyLow/issue/celeborn-1740-add-stacktrace-utils-to-cppClient.

Authored-by: HolyLow <[email protected]>
Signed-off-by: mingji <[email protected]>
  • Loading branch information
HolyLow authored and FMX committed Nov 27, 2024
1 parent e642197 commit 1aefd8f
Show file tree
Hide file tree
Showing 4 changed files with 288 additions and 1 deletion.
2 changes: 2 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,8 @@ Meta Velox
./cpp/scripts/setup-helper-functions.sh
./cpp/celeborn/utils/ProcessBase.h
./cpp/celeborn/utils/ProcessBase.cpp
./cpp/celeborn/utils/StackTrace.h
./cpp/celeborn/utils/StackTrace.cpp


------------------------------------------------------------------------------------
Expand Down
2 changes: 1 addition & 1 deletion cpp/celeborn/utils/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
# 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.
add_library(utils ProcessBase.cpp)
add_library(utils ProcessBase.cpp StackTrace.cpp)

target_link_libraries(
utils
Expand Down
192 changes: 192 additions & 0 deletions cpp/celeborn/utils/StackTrace.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
/*
* Based on StackTrace.cpp from Facebook Velox
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* 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
*
* 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.
*/

#include "celeborn/utils/StackTrace.h"

// Symbolizer requires folly to be compiled with libelf and libdwarf support
// (also currently only works in Linux).
#if __linux__ && FOLLY_HAVE_ELF && FOLLY_HAVE_DWARF
#define CELEBORN_HAS_SYMBOLIZER 1
#else
#define CELEBORN_HAS_SYMBOLIZER 0
#endif

#include <algorithm>
#include <fstream>

#include <fmt/format.h>
#include <folly/Indestructible.h>
#include <folly/String.h>
#include <folly/experimental/symbolizer/StackTrace.h>

#include "utils/ProcessBase.h"

#ifdef __linux__
#include <folly/experimental/symbolizer/Symbolizer.h> // @manual
#include <folly/fibers/FiberManager.h> // @manual
#endif

namespace celeborn::utils {

StackTrace::StackTrace(int32_t skipFrames) {
create(skipFrames);
}

StackTrace::StackTrace(const StackTrace& other) {
bt_pointers_ = other.bt_pointers_;
if (folly::test_once(other.bt_vector_flag_)) {
bt_vector_ = other.bt_vector_;
folly::call_once(bt_vector_flag_, [] {}); // Set the flag.
}
if (folly::test_once(other.bt_flag_)) {
bt_ = other.bt_;
folly::call_once(bt_flag_, [] {}); // Set the flag.
}
}

StackTrace& StackTrace::operator=(const StackTrace& other) {
if (this != &other) {
this->~StackTrace();
new (this) StackTrace(other);
}
return *this;
}

void StackTrace::create(int32_t skipFrames) {
const int32_t kDefaultSkipFrameAdjust = 2; // ::create(), ::StackTrace()
const int32_t kMaxFrames = 75;

bt_pointers_.clear();
uintptr_t btpointers[kMaxFrames];
ssize_t framecount = folly::symbolizer::getStackTrace(btpointers, kMaxFrames);
if (framecount <= 0) {
return;
}

framecount = std::min(framecount, static_cast<ssize_t>(kMaxFrames));
skipFrames = std::max(skipFrames + kDefaultSkipFrameAdjust, 0);

bt_pointers_.reserve(framecount - skipFrames);
for (int32_t i = skipFrames; i < framecount; i++) {
bt_pointers_.push_back(reinterpret_cast<void*>(btpointers[i]));
}
}

///////////////////////////////////////////////////////////////////////////////
// reporting functions

const std::vector<std::string>& StackTrace::toStrVector() const {
folly::call_once(bt_vector_flag_, [&] {
size_t frame = 0;
static folly::Indestructible<folly::fbstring> myname{
folly::demangle(typeid(decltype(*this))) + "::"};
bt_vector_.reserve(bt_pointers_.size());
for (auto ptr : bt_pointers_) {
auto framename = translateFrame(ptr);
if (folly::StringPiece(framename).startsWith(*myname)) {
continue; // ignore frames in the StackTrace class
}
bt_vector_.push_back(fmt::format("# {:<2d} {}", frame++, framename));
}
});
return bt_vector_;
}

const std::string& StackTrace::toString() const {
folly::call_once(bt_flag_, [&] {
const auto& vec = toStrVector();
size_t needed = 0;
for (const auto& frame : vec) {
needed += frame.size() + 1;
}
bt_.reserve(needed);
for (const auto& frame_title : vec) {
bt_ += frame_title;
bt_ += '\n';
}
});
return bt_;
}

std::string StackTrace::log(
const char* errorType,
std::string* out /* = NULL */) const {
std::string pid = folly::to<std::string>(getProcessId());

std::string msg;
msg += "Host: " + getHostName();
msg += "\nProcessID: " + pid;
msg += "\nThreadID: " +
folly::to<std::string>(reinterpret_cast<uintptr_t>(getThreadId()));
msg += "\nName: " + getAppName();
msg += "\nType: ";
if (errorType) {
msg += errorType;
} else {
msg += "(unknown error)";
}
msg += "\n\n";
msg += toString();
msg += "\n";

std::string tracefn = "/tmp/stacktrace." + pid + ".log";
std::ofstream f(tracefn.c_str());
if (f) {
f << msg;
f.close();
}

if (out) {
*out = msg;
}
return tracefn;
}

#if CELEBORN_HAS_SYMBOLIZER
namespace {
inline std::string translateFrameImpl(void* addressPtr) {
// TODO: lineNumbers has been disabled since 2009.
using namespace folly::symbolizer;

std::uintptr_t address = reinterpret_cast<std::uintptr_t>(addressPtr);
Symbolizer symbolizer(LocationInfoMode::DISABLED);
SymbolizedFrame frame;
symbolizer.symbolize(address, frame);

StringSymbolizePrinter printer(SymbolizePrinter::TERSE);
printer.print(frame);
return printer.str();
}
} // namespace
#endif

std::string StackTrace::translateFrame(void* addressPtr, bool /*lineNumbers*/) {
#if CELEBORN_HAS_SYMBOLIZER
return folly::fibers::runInMainContext(
[addressPtr]() { return translateFrameImpl(addressPtr); });
#else
(void)addressPtr;
return std::string{};
#endif
}

std::string StackTrace::demangle(const char* mangled) {
return folly::demangle(mangled).toStdString();
}

} // namespace celeborn::utils
93 changes: 93 additions & 0 deletions cpp/celeborn/utils/StackTrace.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*
* Based on StackTrace.h from Facebook Velox
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* 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
*
* 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

#include <folly/synchronization/CallOnce.h>

#include <string>
#include <vector>

namespace celeborn::utils {

///////////////////////////////////////////////////////////////////////////////

// TODO: Deprecate in favor of folly::symbolizer.
class StackTrace {
public:
/**
* Translate a frame pointer to file name and line number pair.
*/
static std::string translateFrame(void* framePtr, bool lineNumbers = true);

/**
* Demangle a function name.
*/
static std::string demangle(const char* mangled);

public:
/**
* Constructor -- saves the current stack trace. By default, we skip the
* frames for StackTrace::StackTrace. If you want those, you can pass
* '-2' to skipFrames.
*/
explicit StackTrace(int32_t skipFrames = 0);

StackTrace(const StackTrace& other);
StackTrace& operator=(const StackTrace& other);

/**
* Generate an output of the written stack trace.
*/
const std::string& toString() const;

/**
* Generate a vector that for each position has the title of the frame.
*/
const std::vector<std::string>& toStrVector() const;

/**
* Return the raw stack pointers.
*/
const std::vector<void*>& getStack() const {
return bt_pointers_;
}

/**
* Log stacktrace into a file under /tmp. If "out" is not null,
* also store translated stack trace into the variable.
* Returns the name of the generated file.
*/
std::string log(const char* errorType, std::string* out = nullptr) const;

private:
/**
* Record bt pointers.
*/
void create(int32_t skipFrames);

private:
std::vector<void*> bt_pointers_;
mutable folly::once_flag bt_vector_flag_;
mutable std::vector<std::string> bt_vector_;
mutable folly::once_flag bt_flag_;
mutable std::string bt_;
};

///////////////////////////////////////////////////////////////////////////////
} // namespace celeborn::utils

0 comments on commit 1aefd8f

Please sign in to comment.