* Copyright (C) 2025-2025. Huawei Technologies Co., Ltd. All rights reserved.
*
* 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 "utils.h"
#include <glog/logging.h>
#include <glog/stl_logging.h>
#include <cctype>
#include <cstring>
#include <algorithm>
#include <chrono>
#include <fstream>
#include <sstream>
#include <filesystem>
#include <iomanip>
#include <random>
#include <unordered_map>
#include <fcntl.h>
#include <libgen.h>
#include <climits>
#include <unistd.h>
#include <pwd.h>
#include <sys/stat.h>
#include <ifaddrs.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netpacket/packet.h>
#include <pybind11/pybind11.h>
namespace dynolog_npu {
namespace ipc_monitor {
namespace {
template <typename T>
std::string IntToHexStr(T number)
{
std::stringstream strStream;
strStream << std::hex << number;
return strStream.str();
}
}
std::unordered_map<SubModule, std::string> submoduleMap = {
{SubModule::IPC, "IPC"},
};
std::unordered_map<ErrCode, std::string> errCodeMap = {
{ErrCode::SUC, "success"},
{ErrCode::PARAM, "invalid parameter"},
{ErrCode::TYPE, "invalid type"},
{ErrCode::VALUE, "invalid value"},
{ErrCode::PTR, "invalid pointer"},
{ErrCode::INTERNAL, "internal error"},
{ErrCode::MEMORY, "memory error"},
{ErrCode::NOT_SUPPORT, "feature not supported"},
{ErrCode::NOT_FOUND, "resource not found"},
{ErrCode::UNAVAIL, "resource unavailable"},
{ErrCode::SYSCALL, "system call failed"},
{ErrCode::TIMEOUT, "timeout error"},
{ErrCode::PERMISSION, "permission error"},
};
std::string getCurrentTimestamp()
{
auto now = std::chrono::system_clock::now();
auto micros = std::chrono::duration_cast<std::chrono::microseconds>(now.time_since_epoch());
std::ostringstream oss;
std::time_t currentTime = std::chrono::system_clock::to_time_t(now);
std::tm timeInfo;
if (localtime_r(¤tTime, &timeInfo) != nullptr) {
auto milli_time = std::chrono::duration_cast<std::chrono::milliseconds>(micros).count() % 1000;
auto micro_time = micros.count() % 1000;
oss << std::put_time(&timeInfo, "%Y%m%d%H%M%S");
constexpr int kMilliTimeWidth = 3;
oss << std::setw(kMilliTimeWidth) << std::setfill('0') << milli_time;
}
return oss.str();
}
uint64_t getCurrentTimestamp64()
{
auto now = std::chrono::system_clock::now();
auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(now.time_since_epoch());
return ns.count();
}
std::string formatErrorCode(SubModule submodule, ErrCode errorCode)
{
std::ostringstream oss;
oss << "\n[ERROR] " << getCurrentTimestamp() << " (PID:" << getpid() << ")";
oss << "ERR" << std::setw(2) << std::setfill('0') << static_cast<int>(submodule);
oss << std::setw(3) << std::setfill('0') << static_cast<int>(errorCode);
oss << " " << submoduleMap[submodule] << " " << errCodeMap[errorCode];
return oss.str();
};
int32_t GetProcessId()
{
static int32_t pid = []() -> int32_t {
return static_cast<int32_t>(getpid());
}();
return pid;
}
bool ParseProcStat(const std::string& line, std::string& command, int& parentPid)
{
size_t lparen = line.find('(');
size_t rparen = line.rfind(')');
if (lparen == std::string::npos || rparen == std::string::npos || rparen <= lparen + 1) {
LOG(WARNING) << "cannot find command name: " << line;
return false;
}
command = line.substr(lparen + 1, rparen - lparen - 1);
std::string afterCmd = line.substr(rparen + 1);
std::istringstream iss(afterCmd);
std::string state;
int ppid;
if (!(iss >> state >> ppid)) {
LOG(WARNING) << "Failed to parse state/ppid from: " << afterCmd;
return false;
}
parentPid = ppid;
return true;
}
std::pair<int32_t, std::string> GetParentPidAndCommand(int32_t pid)
{
std::string fileName = "/proc/" + std::to_string(pid) + "/stat";
std::ifstream statFile(fileName);
if (!statFile) {
return std::make_pair(0, "");
}
int32_t parentPid = 0;
std::string command;
std::string line;
if (std::getline(statFile, line)) {
bool ret = ParseProcStat(line, command, parentPid);
if (ret) {
return std::make_pair(parentPid, command);
}
}
LOG(WARNING) << "Failed to parse /proc/" << pid << "/stat";
return std::make_pair(0, "");
}
std::vector<std::pair<int32_t, std::string>> GetPidCommandPairsofAncestors()
{
std::vector<std::pair<int32_t, std::string>> process_pids_and_cmds;
process_pids_and_cmds.reserve(MaxParentPids + 1);
int32_t current_pid = GetProcessId();
for (int i = 0; i <= MaxParentPids && (i == 0 || current_pid > 1); i++) {
std::pair<int32_t, std::string> parent_pid_and_cmd = GetParentPidAndCommand(current_pid);
process_pids_and_cmds.push_back(std::make_pair(current_pid, parent_pid_and_cmd.second));
current_pid = parent_pid_and_cmd.first;
}
return process_pids_and_cmds;
}
std::vector<int32_t> GetPids()
{
const auto &pids = GetPidCommandPairsofAncestors();
std::vector<int32_t> res;
res.reserve(pids.size());
for (const auto &pidPair : pids) {
res.push_back(pidPair.first);
}
LOG(INFO) << "Success to get parent pid: " << res;
return res;
}
std::string GenerateUuidV4()
{
static std::random_device randomDevice;
static std::mt19937 gen(randomDevice());
static std::uniform_int_distribution<> dis(0, 15);
static std::uniform_int_distribution<> dis2(8, 11);
std::stringstream stringStream;
stringStream << std::hex;
for (int i = 0; i < 8; i++) {
stringStream << dis(gen);
}
stringStream << "-";
for (int j = 0; j < 4; j++) {
stringStream << dis(gen);
}
stringStream << "-4";
for (int k = 0; k < 3; k++) {
stringStream << dis(gen);
}
stringStream << "-";
stringStream << dis2(gen);
for (int m = 0; m < 3; m++) {
stringStream << dis(gen);
}
stringStream << "-";
for (int n = 0; n < 12; n++) {
stringStream << dis(gen);
}
return stringStream.str();
}
bool Str2Uint32(uint32_t& dest, const std::string& str)
{
if (str.empty()) {
LOG(ERROR) << "Str to uint32 failed, input string is null";
return false;
}
size_t pos = 0;
try {
dest = static_cast<uint32_t>(std::stoul(str, &pos));
} catch(...) {
LOG(ERROR) << "Str to uint32 failed, input string is " << str;
return false;
}
if (pos != str.size()) {
LOG(ERROR) << "Str to uint32 failed, input string is " << str;
return false;
}
return true;
}
bool Str2Int32(int32_t& dest, const std::string& str)
{
if (str.empty()) {
LOG(ERROR) << "Str to int32 failed, input string is null";
return false;
}
size_t pos = 0;
try {
dest = static_cast<int32_t>(std::stol(str, &pos));
} catch(...) {
LOG(ERROR) << "Str to int32 failed, input string is " << str;
return false;
}
if (pos != str.size()) {
LOG(ERROR) << "Str to int32 failed, input string is " << str;
return false;
}
return true;
}
bool Str2Bool(bool& dest, const std::string& str)
{
std::string lower_str = str;
std::transform(lower_str.begin(), lower_str.end(), lower_str.begin(), ::tolower);
if (lower_str == "true" || lower_str == "1") {
dest = true;
return true;
}
if (lower_str == "false" || lower_str == "0") {
dest = false;
return true;
}
LOG(ERROR) << "Str to bool failed, input string is " << str;
return false;
}
std::string& trim(std::string& str)
{
if (str.empty()) {
return str;
}
str.erase(0, str.find_first_not_of(" "));
str.erase(str.find_last_not_of(" ") + 1);
return str;
}
std::vector<std::string> split(const std::string& str, char delimiter)
{
std::vector<std::string> tokens;
std::string token;
std::istringstream tokenStream(str);
while (std::getline(tokenStream, token, delimiter)) {
tokens.push_back(token);
}
return tokens;
}
std::string join(const std::vector<std::string> &strs, const std::string &delimiter)
{
std::stringstream ss;
for (size_t i = 0, len = strs.size(); i < len; ++i) {
ss << strs[i] << (i == len - 1 ? "" : delimiter);
}
return ss.str();
}
void *MsptiMalloc(size_t size, size_t alignment)
{
if (alignment > 0) {
size = (size + alignment - 1) / alignment * alignment;
}
#if defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200112L
void *ptr = nullptr;
if (posix_memalign(&ptr, alignment, size) != 0) {
ptr = nullptr;
}
return ptr;
#else
return malloc(size);
#endif
}
void MsptiFree(uint8_t *ptr)
{
if (ptr != nullptr) {
free(ptr);
}
}
bool PathUtils::IsFileExist(const std::string &path)
{
if (path.empty() || path.size() > PATH_MAX) {
return false;
}
return access(path.c_str(), F_OK) == 0;
}
bool PathUtils::IsFileWritable(const std::string &path)
{
if (path.empty() || path.size() > PATH_MAX) {
return false;
}
return access(path.c_str(), W_OK) == 0;
}
bool PathUtils::IsDir(const std::string &path)
{
if (path.empty() || path.size() > PATH_MAX) {
return false;
}
struct stat st{};
int ret = lstat(path.c_str(), &st);
if (ret != 0) {
return false;
}
return S_ISDIR(st.st_mode);
}
bool PathUtils::CreateDir(const std::string &path)
{
if (path.empty() || path.size() > PATH_MAX) {
return false;
}
if (IsFileExist(path)) {
return IsDir(path);
}
size_t pos = 0;
while ((pos = path.find_first_of('/', pos)) != std::string::npos) {
std::string baseDir = path.substr(0, ++pos);
if (IsFileExist(baseDir)) {
if (IsDir(baseDir)) {
continue;
} else {
return false;
}
}
if (mkdir(baseDir.c_str(), DATA_DIR_AUTHORITY) != 0) {
if (errno != EEXIST) {
return false;
}
}
}
auto ret = mkdir(path.c_str(), DATA_DIR_AUTHORITY);
return (ret == 0 || errno == EEXIST) ? true : false;
}
std::string PathUtils::RealPath(const std::string &path)
{
if (path.empty() || path.size() > PATH_MAX) {
return "";
}
char realPath[PATH_MAX] = {0};
if (realpath(path.c_str(), realPath) == nullptr) {
return "";
}
return std::string(realPath);
}
std::string PathUtils::RelativeToAbsPath(const std::string &path)
{
if (path.empty() || path.size() > PATH_MAX) {
return "";
}
if (path[0] != '/') {
char pwdPath[PATH_MAX] = {0};
if (getcwd(pwdPath, PATH_MAX) != nullptr) {
return std::string(pwdPath) + "/" + path;
}
return "";
}
return std::string(path);
}
std::string PathUtils::DirName(const std::string &path)
{
if (path.empty()) {
return "";
}
std::filesystem::path fsPath(path);
if (fsPath.empty()) {
return "";
}
return fsPath.parent_path().filename().string();
}
bool PathUtils::CreateFile(const std::string &path)
{
if (path.empty() || path.size() > PATH_MAX || !CreateDir(DirName(path))) {
return false;
}
int fd = creat(path.c_str(), DATA_FILE_AUTHORITY);
return (fd < 0 || close(fd) != 0) ? false : true;
}
bool PathUtils::IsSoftLink(const std::string &path)
{
if (path.empty() || path.size() > PATH_MAX || !IsFileExist(path)) {
return false;
}
struct stat st{};
if (lstat(path.c_str(), &st) != 0) {
return false;
}
return S_ISLNK(st.st_mode);
}
bool PathUtils::DirPathCheck(const std::string& path)
{
if (path.empty() || path.size() > PATH_MAX) {
fprintf(stderr, "[ERROR] The length of Path %s is invalid.\n", path.c_str());
return false;
}
if (IsSoftLink(path)) {
fprintf(stderr, "[ERROR] Path %s is soft link.\n", path.c_str());
return false;
}
if (!IsFileExist(path) && !CreateDir(path)) {
fprintf(stderr, "[ERROR] Path %s not exist and create failed.\n", path.c_str());
return false;
}
if (!IsDir(path) || !IsFileWritable(path)) {
fprintf(stderr, "[ERROR] %s is not a directory or is not writable.\n", path.c_str());
return false;
}
return true;
}
bool PathUtils::IsOwner(const std::string &path)
{
if (path.empty() || path.size() > PATH_MAX) {
return false;
}
struct stat info;
if (stat(path.c_str(), &info) != 0) {
LOG(ERROR) << "Get file stat failed, path: " << path;
return false;
}
return info.st_uid == getuid();
}
int GetRankId()
{
static int rankId = []() -> int {
pybind11::gil_scoped_acquire gil;
return pybind11::module::import("IPCMonitor.utils").attr("get_rank_id")().cast<int>();
}();
return rankId;
}
uint64_t CalcHashId(const std::string &data)
{
static const uint32_t UINT32_BITS = 32;
uint32_t prime[2] = {29, 131};
uint32_t hash[2] = {0};
for (char d : data) {
hash[0] = hash[0] * prime[0] + static_cast<uint32_t>(d);
hash[1] = hash[1] * prime[1] + static_cast<uint32_t>(d);
}
return (static_cast<uint64_t>(hash[0]) << UINT32_BITS) | hash[1];
}
std::string GetHostName()
{
char hostName[PATH_MAX] = {0};
if (gethostname(hostName, PATH_MAX) != 0) {
return "";
}
return std::string(hostName);
}
std::string GetHostUid()
{
static const uint8_t SECOND_LEAST_BIT = 1 << 1;
struct ifaddrs *ifaddr = nullptr;
if (getifaddrs(&ifaddr) == -1) {
if (ifaddr != nullptr) {
freeifaddrs(ifaddr);
}
return 0;
}
std::vector<std::string> universalMacAddrs;
std::vector<std::string> localMacAddrs;
for (struct ifaddrs *ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) {
if (ifa->ifa_addr == nullptr || ifa->ifa_addr->sa_family != AF_PACKET) {
continue;
}
if ((ifa->ifa_flags & IFF_LOOPBACK) != 0) {
continue;
}
struct sockaddr_ll *lladdr = ReinterpretConvert<struct sockaddr_ll*>(ifa->ifa_addr);
uint32_t len = static_cast<uint32_t>(lladdr->sll_halen);
if (len > 0) {
std::string addr;
for (uint32_t i = 0; i < len; ++i) {
std::string hexAddr = IntToHexStr(static_cast<uint16_t>(lladdr->sll_addr[i]));
addr += (hexAddr.length() > 1) ? hexAddr : ("0" + hexAddr);
}
if ((lladdr->sll_addr[0] & SECOND_LEAST_BIT) == 0) {
universalMacAddrs.emplace_back(addr);
} else {
localMacAddrs.emplace_back(addr);
}
}
}
if (ifaddr != nullptr) {
freeifaddrs(ifaddr);
}
if (universalMacAddrs.empty() && localMacAddrs.empty()) {
return 0;
}
auto &macAddrs = universalMacAddrs.empty() ? localMacAddrs : universalMacAddrs;
std::sort(macAddrs.begin(), macAddrs.end());
return std::to_string(CalcHashId(join(macAddrs, "-")));
}
bool CreateMsmonitorLogPath(std::string &path)
{
const char* logPathEnvVal = getenv("MSMONITOR_LOG_PATH");
std::string logPath;
if (logPathEnvVal != nullptr) {
logPath = logPathEnvVal;
}
if (logPath.empty()) {
char cwdPath[PATH_MAX] = {0};
if (getcwd(cwdPath, PATH_MAX) != nullptr) {
logPath = cwdPath;
}
}
if (logPath.empty()) {
fprintf(stderr, "[ERROR] Failed to get msmonitor log path.\n");
return false;
}
logPath = logPath + "/msmonitor_log";
std::string absPath = PathUtils::RelativeToAbsPath(logPath);
if (PathUtils::DirPathCheck(absPath)) {
std::string realPath = PathUtils::RealPath(absPath);
if (PathUtils::CreateDir(realPath)) {
path = realPath;
return true;
}
fprintf(stderr, "[ERROR] Create LOG_PATH: %s failed.\n", realPath.c_str());
} else {
fprintf(stderr, "[ERROR] LOG_PATH: %s of Msmonitor is invalid.\n", absPath.c_str());
}
return false;
}
std::string GetCurrentUserHomePath()
{
static std::string homePath = []() -> std::string {
struct passwd *pw = getpwuid(getuid());
if (pw == nullptr || pw->pw_dir == nullptr) {
LOG(ERROR) << "Failed to get current user info";
return "";
}
return std::string(pw->pw_dir);
}();
return homePath;
}
}
}