Skip to content
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,8 @@ if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "^(AppleClang|Clang|GNU)$")
add_flag(-Werror-non-virtual-dtor) # warn the user if a class with virtual functions has a non-virtual destructor. This helps catch hard to track down memory errors
add_flag(-Werror-sign-compare) # warn the user if they compare a signed and unsigned numbers
add_flag(-Werror-reorder) # field '$1' will be initialized after field '$2'

add_link_options(-rdynamic)
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
# using Visual Studio C++
# TODO(warchant): add flags https://github.com/lefticus/cppbestpractices/blob/master/02-Use_the_Tools_Available.md#msvc
Expand Down
91 changes: 91 additions & 0 deletions include/libp2p/muxer/yamux/hardware_tracker.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
#pragma once

#include <memory>
#include <atomic>
#include <signal.h>
#include <sys/ptrace.h>
#include <sys/user.h>
#include <sys/wait.h>
#include <unistd.h>
#include <execinfo.h>
#include <iostream>
#include <cstring>

namespace libp2p::connection {

class YamuxedConnection;

class HardwareSharedPtrTracker {
public:
static HardwareSharedPtrTracker& getInstance() {
static HardwareSharedPtrTracker instance;
return instance;
}

// Start tracking the reference count of a shared_ptr
void startTracking(const std::shared_ptr<YamuxedConnection>& ptr);

// Stop current tracking
void stopTracking();

// Check if tracking is active
bool isTracking() const { return is_tracking_; }

// Enable/disable tracking
void enable() { enabled_ = true; }
void disable() { enabled_ = false; }

private:
HardwareSharedPtrTracker();
~HardwareSharedPtrTracker();

// Get the address of the reference count in a shared_ptr
void* getRefCountAddress(const std::shared_ptr<YamuxedConnection>& ptr);

// Set hardware watchpoint
bool setHardwareWatchpoint(void* address);

// Remove hardware watchpoint
bool removeHardwareWatchpoint();

// Signal handler for memory changes
static void signalHandler(int sig, siginfo_t* info, void* context);

// Print stack trace
void printStackTrace();

// Check counter and switch to the next object if needed
void checkAndSwitchIfNeeded();

private:
std::atomic<bool> enabled_{false};
std::atomic<bool> is_tracking_{false};
std::atomic<bool> should_stop_{false}; // Flag for signal handler

void* watched_address_{nullptr};
int watchpoint_fd_{-1}; // Store perf_event fd
std::weak_ptr<YamuxedConnection> current_tracked_ptr_;

// For debug registers
static constexpr int DR7_L0 = 1; // Local enable for DR0
static constexpr int DR7_RW0_WRITE = (1 << 16); // Watch writes to DR0
static constexpr int DR7_LEN0_4BYTES = (3 << 18); // 4-byte length for DR0

static HardwareSharedPtrTracker* instance_;
struct sigaction old_sigtrap_action_;
};

// Global function for setting in yamux.cpp
void trackNextYamuxedConnection(const std::shared_ptr<YamuxedConnection>& ptr);

// Macros for convenience
#define YAMUX_HARDWARE_TRACK_ENABLE() \
libp2p::connection::HardwareSharedPtrTracker::getInstance().enable()

#define YAMUX_HARDWARE_TRACK_DISABLE() \
libp2p::connection::HardwareSharedPtrTracker::getInstance().disable()

#define YAMUX_HARDWARE_TRACK_SHARED_PTR(ptr) \
libp2p::connection::trackNextYamuxedConnection(ptr)

} // namespace libp2p::connection
2 changes: 1 addition & 1 deletion include/libp2p/muxer/yamux/yamux_stream.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ namespace libp2p::connection {
closeCompleted();

/// Underlying connection (secured)
std::shared_ptr<connection::SecureConnection> connection_;
std::weak_ptr<connection::SecureConnection> connection_;

/// Yamux-specific interface of connection
YamuxStreamFeedback &feedback_;
Expand Down
12 changes: 12 additions & 0 deletions include/libp2p/muxer/yamux/yamuxed_connection.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,16 @@ namespace libp2p::connection {
ReadCallbackFunc cb) override;
void deferWriteCallback(std::error_code ec, WriteCallbackFunc cb) override;

void markAsRegistered();

size_t getStreamsCount() const { return streams_.size(); }
size_t getPendingStreamsCount() const { return pending_outbound_streams_.size(); }
long getSharedPtrUseCount() const { return shared_from_this().use_count(); }

void debugPrintActiveStreams() const;

void debugPrintMemoryLeakSources() const;

private:
using Streams = std::unordered_map<StreamId, std::shared_ptr<YamuxStream>>;

Expand Down Expand Up @@ -241,6 +251,8 @@ namespace libp2p::connection {

bool close_after_write_ = false;

bool registered_in_manager_ = false;

public:
LIBP2P_METRICS_INSTANCE_COUNT_IF_ENABLED(
libp2p::connection::YamuxedConnection);
Expand Down
7 changes: 7 additions & 0 deletions include/libp2p/network/impl/connection_manager_impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#pragma once

#include <unordered_set>
#include <mutex>

#include <libp2p/event/bus.hpp>
#include <libp2p/network/connection_manager.hpp>
Expand Down Expand Up @@ -46,6 +47,12 @@ namespace libp2p::network {
/// Reentrancy resolver between closeConnectionsToPeer and
/// onConnectionClosed
boost::optional<peer::PeerId> closing_connections_to_peer_;

/// Mutex to protect connection_is_closing_ set
std::mutex connection_mutex_;

/// Set of connections currently being closed to prevent double closing
std::unordered_set<ConnectionSPtr> connection_is_closing_;
};

} // namespace libp2p::network
1 change: 1 addition & 0 deletions src/muxer/yamux/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ libp2p_add_library(p2p_yamuxed_connection
yamux_frame.cpp
yamux_stream.cpp
yamux_reading_state.cpp
hardware_tracker.cpp
)
target_link_libraries(p2p_yamuxed_connection
Boost::boost
Expand Down
Loading
Loading