Skip to content

Commit

Permalink
Reduce memory allocation of the profiler
Browse files Browse the repository at this point in the history
  • Loading branch information
gleocadie committed Feb 7, 2023
1 parent 2c2b876 commit 45fb62f
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 32 deletions.
Expand Up @@ -9,14 +9,17 @@
#include <sys/syscall.h>
#include "OsSpecificApi.h"
#include "OpSysTools.h"
#include "ScopeFinalizer.h"

#include "IConfiguration.h"
#include "Log.h"
#include "LinuxStackFramesCollector.h"
#include "OpSysTools.h"
#include "ProfilerSignalManager.h"
#include "StackFramesCollectorBase.h"
#include "shared/src/native-src/loader.h"


namespace OsSpecificApi {
std::unique_ptr<StackFramesCollectorBase> CreateNewStackFramesCollectorInstance(ICorProfilerInfo4* pCorProfilerInfo, IConfiguration const* const pConfiguration)
{
Expand Down Expand Up @@ -48,35 +51,32 @@ std::unique_ptr<StackFramesCollectorBase> CreateNewStackFramesCollectorInstance(
// pthread_getcpuclockid(pthread_self(), &clockid);
// if (clock_gettime(clockid, &cpu_time)) { ... }
//
static bool firstError = true;

bool GetCpuInfo(pid_t tid, bool& isRunning, uint64_t& cpuTime)
{
char statPath[64];
char statPath[64] = {'\0'};
snprintf(statPath, sizeof(statPath), "/proc/self/task/%d/stat", tid);

// load the line to be able to parse it in memory
std::ifstream file;
file.open(statPath);
std::string sline;
std::getline(file, sline);
file.close();
if (sline.empty())
FILE* fileStream = fopen(statPath, "r");
on_leave { fclose(fileStream); };

if (fileStream == nullptr)
{
return false;
}

char state = ' ';
int32_t userTime = 0;
int32_t kernelTime = 0;
bool success = OpSysTools::ParseThreadInfo(sline, state, userTime, kernelTime);
bool success = OpSysTools::ParseThreadInfo(fileStream, state, userTime, kernelTime);
if (!success)
{
static bool firstError = true;
// log the first error to be able to analyze unexpected string format
if (firstError)
{
firstError = false;
Log::Error("Unexpected /proc/self/task/", tid, "/stat: ", sline);
Log::Error("Unexpected ", statPath);
}

return false;
Expand Down
24 changes: 15 additions & 9 deletions profiler/src/ProfilerEngine/Datadog.Profiler.Native/OpSysTools.h
Expand Up @@ -48,7 +48,8 @@ class OpSysTools final
static std::string GetHostname();
static std::string GetProcessName();

static bool ParseThreadInfo(std::string line, char& state, int32_t& userTime, int32_t& kernelTime)
#ifdef LINUX
static bool ParseThreadInfo(FILE* file, char& state, int32_t& userTime, int32_t& kernelTime)
{
// based on https://linux.die.net/man/5/proc
// state = 3rd position and 'R' for Running
Expand All @@ -57,17 +58,22 @@ class OpSysTools final

// The thread name is in second position and wrapped by ()
// Since the name can contain SPACE and () characters, skip it before scanning the values
auto pos = line.find_last_of(")");
const char* pEnd = line.c_str() + pos + 1;
char line[1024] = {'\0'};

#ifdef _WINDOWS
bool result = sscanf_s(pEnd, " %c %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %d %d", &state, 1, &userTime, &kernelTime) == 3;
#else
bool result = sscanf(pEnd, " %c %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %d %d", &state, &userTime, &kernelTime) == 3;
#endif
auto length = fread(line, 1, sizeof(line), file);
if (ferror(file) || length == 0)
{
return false;
}

auto pos = strrchr(line, ')');

return result;
// According to the man, thread name must be wrapped by (), so no need to check
// for nullity.

return sscanf(pos + 1, " %c %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %d %d", &state, &userTime, &kernelTime) == 3;
}
#endif

static bool IsSafeToStartProfiler(double coresThreshold);
static std::int64_t GetHighPrecisionTimestamp();
Expand Down
55 changes: 43 additions & 12 deletions profiler/test/Datadog.Profiler.Native.Tests/OpSysToolsTest.cpp
@@ -1,27 +1,56 @@
// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License.
// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2022 Datadog, Inc.

#ifdef LINUX

#include "gmock/gmock.h"
#include "gtest/gtest.h"


#include "OpSysTools.h"

#include "shared/src/native-src/dd_filesystem.hpp"

#include <stdio.h>

// based on https://linux.die.net/man/5/proc
// -------------------------------------------
// state = 3rd position and 'R' for Running
// user = 14th position in clock ticks
// kernel = 15th position in clock ticks
//

class CFile
{
public:
CFile(char const* content)
{
_file = tmpfile();
fputs(content, _file);
rewind(_file);
}

~CFile()
{
fclose(_file);
}

operator FILE*() const
{
return _file;
}

private:
FILE* _file;
};

TEST(GetThreadInfoTest, Check_EmptyThreadName)
{
std::string line = "377 () R 46 369 46 34817 369 4194368 95 0 0 0 1862 609 0 0 20 ";
CFile file("377 () R 46 369 46 34817 369 4194368 95 0 0 0 1862 609 0 0 20 ");
char state = ' ';
int userTime = 0;
int kernelTime = 0;

bool result = OpSysTools::ParseThreadInfo(line, state, userTime, kernelTime);
bool result = OpSysTools::ParseThreadInfo(file, state, userTime, kernelTime);

ASSERT_TRUE(result);
ASSERT_EQ('R', state);
Expand All @@ -31,12 +60,12 @@ TEST(GetThreadInfoTest, Check_EmptyThreadName)

TEST(GetThreadInfoTest, Check_NoSpaceThreadName)
{
std::string line = "377 (ThreadNameWithoutSpace) R 46 369 46 34817 369 4194368 95 0 0 0 1862 609 0 0 20 ";
CFile file("377 (ThreadNameWithoutSpace) R 46 369 46 34817 369 4194368 95 0 0 0 1862 609 0 0 20 ");
char state = ' ';
int userTime = 0;
int kernelTime = 0;

bool result = OpSysTools::ParseThreadInfo(line, state, userTime, kernelTime);
bool result = OpSysTools::ParseThreadInfo(file, state, userTime, kernelTime);

ASSERT_TRUE(result);
ASSERT_EQ('R', state);
Expand All @@ -46,12 +75,12 @@ TEST(GetThreadInfoTest, Check_NoSpaceThreadName)

TEST(GetThreadInfoTest, Check_SpaceInThreadName)
{
std::string line = "377 (Thread Name With Space) R 46 369 46 34817 369 4194368 95 0 0 0 1862 609 0 0 20 ";
CFile file("377 (Thread Name With Space) R 46 369 46 34817 369 4194368 95 0 0 0 1862 609 0 0 20 ");
char state = ' ';
int userTime = 0;
int kernelTime = 0;

bool result = OpSysTools::ParseThreadInfo(line, state, userTime, kernelTime);
bool result = OpSysTools::ParseThreadInfo(file, state, userTime, kernelTime);

ASSERT_TRUE(result);
ASSERT_EQ('R', state);
Expand All @@ -61,12 +90,12 @@ TEST(GetThreadInfoTest, Check_SpaceInThreadName)

TEST(GetThreadInfoTest, Check_SpaceAndParenthesisInThreadName)
{
std::string line = "377 (la (la) )) land) )) R 46 369 46 34817 369 4194368 95 0 0 0 1862 609 0 0 20 ";
CFile file("377 (la (la) )) land) )) R 46 369 46 34817 369 4194368 95 0 0 0 1862 609 0 0 20 ");
char state = ' ';
int userTime = 0;
int kernelTime = 0;

bool result = OpSysTools::ParseThreadInfo(line, state, userTime, kernelTime);
bool result = OpSysTools::ParseThreadInfo(file, state, userTime, kernelTime);

ASSERT_TRUE(result);
ASSERT_EQ('R', state);
Expand All @@ -76,15 +105,17 @@ TEST(GetThreadInfoTest, Check_SpaceAndParenthesisInThreadName)

TEST(GetThreadInfoTest, Check_SpaceAndParenthesisInThreadName_And_FinalParenthesis)
{
std::string line = "377 (la (la) )) land) )) R 46 369 46 34817 369 4194368 95 0 0 0 1862 609 0 0 20)";
CFile file("377 (la (la) )) land) )) R 46 369 46 34817 369 4194368 95 0 0 0 1862 609 0 0 20)");
char state = ' ';
int userTime = 0;
int kernelTime = 0;

bool result = OpSysTools::ParseThreadInfo(line, state, userTime, kernelTime);
bool result = OpSysTools::ParseThreadInfo(file, state, userTime, kernelTime);

ASSERT_FALSE(result);
ASSERT_EQ(' ', state);
ASSERT_EQ(0, userTime);
ASSERT_EQ(0, kernelTime);
}
}

#endif

0 comments on commit 45fb62f

Please sign in to comment.