Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions include/dso_hdr.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@
#include <array>
#include <cassert>
#include <map>
#include <optional>
#include <string>
#include <string_view>
#include <unordered_map>
#include <unordered_set>

Expand Down Expand Up @@ -216,6 +218,9 @@ class DsoHdr {
FileInfoId_t update_id_dd_profiling(const Dso &dso);

FileInfoId_t update_id_from_path(const Dso &dso);
std::optional<std::string>
remap_host_workspace_path(std::string_view original) const;
void init_workspace_remap();

// Unordered map (by pid) of sorted DSOs
DsoPidMap _pid_map;
Expand All @@ -224,6 +229,8 @@ class DsoHdr {
FileInfoVector _file_info_vector;
std::string _path_to_proc; // /proc files can be mounted at various places
// (whole host profiling)
std::string _workspace_host_prefix;
std::string _workspace_container_root;
int _dd_profiling_fd;
// Assumption is that we have a single version of the dd_profiling library
// across all PIDs.
Expand Down
61 changes: 61 additions & 0 deletions src/dso_hdr.cc
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

#include <algorithm>
#include <cassert>
#include <cstdlib>
#include <cstring>
#include <fcntl.h>
#include <memory>
Expand Down Expand Up @@ -109,6 +110,7 @@ DsoHdr::DsoHdr(std::string_view path_to_proc, int dd_profiling_fd)
} else {
_path_to_proc = path_to_proc;
}
init_workspace_remap();
// 0 element is error element
_file_info_vector.emplace_back(FileInfo(), 0);
}
Expand Down Expand Up @@ -528,6 +530,57 @@ Dso DsoHdr::dso_from_proc_line(int pid, const char *line) {
DsoOrigin::kProcMaps};
}

void DsoHdr::init_workspace_remap() {
_workspace_host_prefix.clear();
_workspace_container_root.clear();
if (const char *workspace_env = std::getenv("DDPROF_WORKSPACE_ROOT");
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

todo: naming needs to be adjusted

workspace_env && workspace_env[0] != '\0') {
const std::string mapping = workspace_env;
auto const sep = mapping.find('|');
if (sep != std::string::npos) {
_workspace_host_prefix = mapping.substr(0, sep);
_workspace_container_root = mapping.substr(sep + 1);
} else {
LG_WRN("DDPROF_WORKSPACE_ROOT is expected to be "
"'<host_prefix>|<container_root>'");
}
}
auto trim_trailing_slash = [](std::string &path) {
while (path.size() > 1 && path.back() == '/') {
path.pop_back();
}
};
trim_trailing_slash(_workspace_host_prefix);
trim_trailing_slash(_workspace_container_root);
if (_workspace_host_prefix.empty() || _workspace_container_root.empty()) {
_workspace_host_prefix.clear();
_workspace_container_root.clear();
} else {
LG_NTC("DDPROF_WORKSPACE_ROOT is set to %s|%s",
_workspace_host_prefix.c_str(), _workspace_container_root.c_str());
}
}

// On macOS with Docker / virtiofs, /proc/pid/maps exposes host-backed paths
// (e.g. /run/host_virtiofs/Users/foo/ddprof/build/...) instead of the
// in-container path (/app/build/...). When DDPROF_WORKSPACE_ROOT is set,
// strip the host prefix and replace it with the container root so that
// libdwfl can locate the ELF file.
std::optional<std::string>
DsoHdr::remap_host_workspace_path(std::string_view original) const {
if (_workspace_host_prefix.empty()) {
return std::nullopt;
}
if (!original.starts_with(_workspace_host_prefix)) {
return std::nullopt;
}
auto suffix = original.substr(_workspace_host_prefix.size());
if (!suffix.empty() && suffix[0] != '/') {
return std::nullopt;
}
return _workspace_container_root + std::string(suffix);
}

FileInfo DsoHdr::find_file_info(const Dso &dso) {
int64_t size;
inode_t inode;
Expand All @@ -551,6 +604,14 @@ FileInfo DsoHdr::find_file_info(const Dso &dso) {
return {proc_path, size, inode};
}

if (auto remapped = remap_host_workspace_path(dso._filename)) {
if (get_file_inode(remapped->c_str(), &inode, &size)) {
LG_DBG("[DSO] Remapped %s -> %s", dso._filename.c_str(),
remapped->c_str());
return {*remapped, size, inode};
}
}

LG_DBG("[DSO] Unable to find path to %s", dso._filename.c_str());
return {};
}
Expand Down
2 changes: 1 addition & 1 deletion test/ddprof_stats-ut.cc
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ TEST(ddprof_statsTest, ConnectAndSet) {
}

TEST(ddprof_statsTest, Arithmetic) {
const char path_listen[] = UNIT_TEST_DATA "/my_statsd_listener.sock";
const char path_listen[] = "/tmp/my_statsd_listener_arithmetic";
unlink(path_listen); // make sure node is available, OK if this fails

// Initiate "server"
Expand Down
39 changes: 39 additions & 0 deletions test/dso-ut.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,15 @@

#include "dso_hdr.hpp"

#include <cstdlib>
#include <filesystem>
#include <fstream>
#include <gtest/gtest.h>
#include <pthread.h>
#include <string>
#include <sys/mman.h>
#include <unistd.h>
#include <vector>

#include "defer.hpp"
#include "loghandle.hpp"
Expand Down Expand Up @@ -393,6 +398,40 @@ TEST(DSOTest, missing_dso) {
EXPECT_FALSE(file_info._inode);
}

TEST(DSOTest, WorkspaceRemapping) {
std::string base_template =
std::filesystem::temp_directory_path() / "ddprof-workspace-remapXXXXXX";
std::vector<char> tmpl(base_template.begin(), base_template.end());
tmpl.push_back('\0');
char *tmp_dir = mkdtemp(tmpl.data());
ASSERT_NE(tmp_dir, nullptr);
auto cleanup_dir =
make_defer([&]() { std::filesystem::remove_all(tmp_dir); });

std::string container_root = tmp_dir;
std::string relative_path = "/subdir/test_binary";
std::filesystem::create_directories(container_root + "/subdir");

std::string container_file = container_root + relative_path;
{
std::ofstream ofs(container_file);
ofs << "test";
}

std::string host_prefix = "/run/host_virtiofs/Users/test/ddprof";
std::string host_path = host_prefix + relative_path;
std::string env_value = host_prefix + "|" + container_root;
setenv("DDPROF_WORKSPACE_ROOT", env_value.c_str(), 1);
auto cleanup_env = make_defer([]() { unsetenv("DDPROF_WORKSPACE_ROOT"); });

DsoHdr dso_hdr;
Dso remap_dso(getpid(), 0x1000, 0x2000, 0, std::move(host_path));
FileInfo file_info = dso_hdr.find_file_info(remap_dso);

EXPECT_EQ(file_info._path, container_file);
EXPECT_NE(file_info._inode, 0);
}

// clang-format off
// Assuming we get a big insertion
// <DEBUG>Dec 14 14:15:16 ddprof[725]: <0>(MAP)722: /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.25 (7f51f1d42000/389000/0)
Expand Down
39 changes: 30 additions & 9 deletions tools/launch_local_build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,15 @@ if [ -e "${CURRENTDIR}/.env" ]; then
fi

MOUNT_CMD="-v ${DEFAULT_DEV_WORKSPACE:-${CURRENTDIR}}:/app"
# avoid symlinks
HOST_SHARE_PATH=$(python3 - <<'PY'
import os, sys
path = os.environ.get("DEFAULT_DEV_WORKSPACE", os.environ.get("CURRENTDIR"))
if not path:
path = os.getcwd()
print(os.path.realpath(path), end="")
PY
)

# Support docker sync : Improves compilation speed
# Example of config (to be pasted in the docker-sync.yml file)
Expand All @@ -102,6 +111,7 @@ if [ -e "${CURRENTDIR}/docker-sync.yml" ]; then
VOLUME_SYNC=$(grep -A 1 "syncs:" "${CURRENTDIR}/docker-sync.yml" | tail -n 1 | awk -F ':' '{print $1}' | sed "s/ //g")
echo "$VOLUME_SYNC"
MOUNT_CMD="--mount source=${VOLUME_SYNC},target=/app"
HOST_SHARE_PATH=""
if ! docker-sync list | grep -q "$VOLUME_SYNC"; then
echo "Please generate a volume: $VOLUME_SYNC"
echo "Suggested commands:"
Expand Down Expand Up @@ -130,13 +140,15 @@ if [ $PERFORM_CLEAN -eq 1 ]; then
fi

# Check if base image exists
if [ ! ${CUSTOM_ID:-,,} == "yes" ] && ! docker images | awk '{print $1}'| grep -qE "^${DOCKER_NAME}$"; then
echo "Building image"
BUILD_CMD="docker build $CACHE_OPTION -t ${DOCKER_NAME} --build-arg COMPILER=$COMPILER --build-arg UBUNTU_VERSION=${UBUNTU_VERSION} -f $BASE_DOCKERFILE ."
#echo "${BUILD_CMD}"
eval "${BUILD_CMD}"
else
echo "Base image found, not rebuilding. Remove it to force rebuild."
if [ ! ${CUSTOM_ID:-,,} == "yes" ]; then
if ! docker image inspect "${DOCKER_NAME}" >/dev/null 2>&1; then
echo "Building image"
BUILD_CMD="docker build $CACHE_OPTION -t ${DOCKER_NAME} --build-arg COMPILER=$COMPILER --build-arg UBUNTU_VERSION=${UBUNTU_VERSION} -f $BASE_DOCKERFILE ."
#echo "${BUILD_CMD}"
eval "${BUILD_CMD}"
else
echo "Base image found, not rebuilding. Remove it to force rebuild."
fi
fi

if [[ $OSTYPE == darwin* ]]; then
Expand All @@ -162,6 +174,15 @@ else
MOUNT_TOOLS_DIR=""
fi

CMD="docker run -it --rm -u $(id -u):$(id -g) --network=host -w /app ${MOUNT_SSH_AGENT} ${MOUNT_TOOLS_DIR} --cap-add CAP_SYS_PTRACE --cap-add SYS_ADMIN ${MOUNT_CMD} \"${DOCKER_NAME}${DOCKER_TAG}\" /bin/bash"
WORKSPACE_ENV=""
if [[ $OSTYPE == darwin* ]] && [ -n "${HOST_SHARE_PATH}" ]; then
HOST_PREFIX="/run/host_virtiofs${HOST_SHARE_PATH}"
CONTAINER_ROOT="/app"
WORKSPACE_ENV="-e DDPROF_WORKSPACE_ROOT=${HOST_PREFIX}|${CONTAINER_ROOT}"
fi

eval "$CMD"
# shellcheck disable=SC2086
docker run -it --rm -u "$(id -u):$(id -g)" --network=host -w /app \
${MOUNT_SSH_AGENT} ${MOUNT_TOOLS_DIR} ${WORKSPACE_ENV} \
--cap-add CAP_SYS_PTRACE --cap-add SYS_ADMIN ${MOUNT_CMD} \
"${DOCKER_NAME}${DOCKER_TAG}" /bin/bash