diff --git a/core/common/ProcParser.cpp b/core/common/ProcParser.cpp index 309119c2ca..348fd33fed 100644 --- a/core/common/ProcParser.cpp +++ b/core/common/ProcParser.cpp @@ -12,48 +12,49 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include -#include -#include -#include +#include #include -#include + #include -#include -#include +#include +#include +#include #include +#include +#include +#include #if defined(__linux__) #include #endif -#include "ProcParser.h" #include "Logger.h" +#include "ProcParser.h" namespace logtail { std::vector Status::ConvertToInt(const std::vector& ids) const { std::vector result; for (const auto& id : ids) { - try { - auto num = std::stoi(id); - result.push_back(num); - } catch (std::out_of_range &e) { - result.push_back(UINT32_MAX); - LOG_WARNING(sLogger, ("[ConvertToInt] vec id", id) ("e", e.what())); - } + try { + auto num = std::stoi(id); + result.push_back(num); + } catch (std::out_of_range& e) { + result.push_back(UINT32_MAX); + LOG_WARNING(sLogger, ("[ConvertToInt] vec id", id)("e", e.what())); + } } return result; - } +} uint32_t Status::ConvertToInt(const std::string& id) const { try { - unsigned long res = std::stoul(id); - return static_cast(res); - } catch (std::out_of_range &e) { - LOG_WARNING(sLogger, ("[ConvertToInt] u32 id", id) ("e", e.what())); - } catch (std::invalid_argument & ee) { - LOG_WARNING(sLogger, ("[ConvertToInt] u32 id", id) ("e", ee.what())); + unsigned long res = std::stoul(id); + return static_cast(res); + } catch (std::out_of_range& e) { + LOG_WARNING(sLogger, ("[ConvertToInt] u32 id", id)("e", e.what())); + } catch (std::invalid_argument& ee) { + LOG_WARNING(sLogger, ("[ConvertToInt] u32 id", id)("e", ee.what())); } return UINT32_MAX; } @@ -61,204 +62,205 @@ uint32_t Status::ConvertToInt(const std::string& id) const { std::string getLastPathSegment(const std::string& path) { size_t pos = path.find_last_of('/'); if (pos == std::string::npos) { - return path; // No '/' found, return the entire string + return path; // No '/' found, return the entire string } else { - return path.substr(pos + 1); // Return the substring after the last '/' + return path.substr(pos + 1); // Return the substring after the last '/' } } int GuessContainerIdOffset() { - const std::string cgroupFilePath = "/proc/self/cgroup"; - std::ifstream cgroupFile(cgroupFilePath); - const std::regex regex("[a-f0-9]{64}"); - std::smatch match; - - std::string line; - std::string lastSegment; - while (std::getline(cgroupFile, line)) { - // cgroup file format:::/ - LOG_DEBUG(sLogger, ("cgroup line", line)); - size_t lastColonPosition = line.find_last_of(':'); - if (lastColonPosition != std::string::npos) { - std::string cgroupPath = line.substr(lastColonPosition + 1); - lastSegment = getLastPathSegment(cgroupPath); - LOG_DEBUG(sLogger, ("The last segment in the cgroup path", lastSegment)); - if (std::regex_search(lastSegment, match, regex)) { - auto cid = match.str(0); - LOG_DEBUG(sLogger, ("lastSegment", line) ("pos", match.position()) ("cid", cid) ("size", cid.size())); - cgroupFile.close(); - return match.position(); - } else { - LOG_DEBUG(sLogger, ("Find next line, Current line unexpected format in cgroup line", lastSegment)); - continue; - } - } - } - cgroupFile.close(); - LOG_ERROR(sLogger, ("No valid cgroup line to parse ... ", "")); - return -1; + const std::string cgroupFilePath = "/proc/self/cgroup"; + std::ifstream cgroupFile(cgroupFilePath); + const std::regex regex("[a-f0-9]{64}"); + std::smatch match; + + std::string line; + std::string lastSegment; + while (std::getline(cgroupFile, line)) { + // cgroup file format:::/ + LOG_DEBUG(sLogger, ("cgroup line", line)); + size_t lastColonPosition = line.find_last_of(':'); + if (lastColonPosition != std::string::npos) { + std::string cgroupPath = line.substr(lastColonPosition + 1); + lastSegment = getLastPathSegment(cgroupPath); + LOG_DEBUG(sLogger, ("The last segment in the cgroup path", lastSegment)); + if (std::regex_search(lastSegment, match, regex)) { + auto cid = match.str(0); + LOG_DEBUG(sLogger, ("lastSegment", line)("pos", match.position())("cid", cid)("size", cid.size())); + cgroupFile.close(); + return match.position(); + } else { + LOG_DEBUG(sLogger, ("Find next line, Current line unexpected format in cgroup line", lastSegment)); + continue; + } + } + } + cgroupFile.close(); + LOG_ERROR(sLogger, ("No valid cgroup line to parse ... ", "")); + return -1; } std::filesystem::path ProcParser::ProcPidPath(uint32_t pid, const std::string& subpath) const { - try { - return std::filesystem::path(proc_path_) / std::to_string(pid) / subpath; - } catch (std::exception &exception) { - return std::filesystem::path(); - } + try { + return std::filesystem::path(proc_path_) / std::to_string(pid) / subpath; + } catch (std::exception& exception) { + return std::filesystem::path(); + } } -std::string ProcParser::ReadPIDLink(uint32_t pid, const std::string &filename) const { - const auto fpath = ProcPidPath(pid, filename); - try { - return std::filesystem::read_symlink(fpath).string(); - } catch (std::filesystem::filesystem_error &e) { - LOG_DEBUG(sLogger, ("[ReadPIDLink] failed pid", pid)("filename", filename)("e", e.what())); - return ""; - } +std::string ProcParser::ReadPIDLink(uint32_t pid, const std::string& filename) const { + const auto fpath = ProcPidPath(pid, filename); + try { + return std::filesystem::read_symlink(fpath).string(); + } catch (std::filesystem::filesystem_error& e) { + LOG_DEBUG(sLogger, ("[ReadPIDLink] failed pid", pid)("filename", filename)("e", e.what())); + return ""; + } } -std::string ProcParser::ReadPIDFile(uint32_t pid, const std::string &filename, const std::string &delimiter) const { -// const auto fpath = ProcPidPath(pid, filename); - std::filesystem::path procRoot = proc_path_; - std::filesystem::path fpath = procRoot / std::to_string(pid) / filename; - std::ifstream ifs(fpath); - if (!ifs) { - return ""; - } - std::string line = ""; - std::string res = ""; - while (std::getline(ifs, line)) { - if (delimiter == "") { - res += std::move(line); - } else { - res += delimiter + std::move(line); - } - } - // Strip out extra null character at the end of the string. - if (!res.empty() && res[res.size() - 1] == 0) { - res.pop_back(); - } - // Replace all nulls with spaces. Sometimes the command line has - // null to separate arguments and others it has spaces. We just make them all spaces - // and leave it to upstream code to tokenize properly. - std::replace(res.begin(), res.end(), static_cast(0), ' '); - return res; +std::string ProcParser::ReadPIDFile(uint32_t pid, const std::string& filename, const std::string& delimiter) const { + // const auto fpath = ProcPidPath(pid, filename); + std::filesystem::path procRoot = proc_path_; + std::filesystem::path fpath = procRoot / std::to_string(pid) / filename; + std::ifstream ifs(fpath); + if (!ifs) { + return ""; + } + std::string line = ""; + std::string res = ""; + while (std::getline(ifs, line)) { + if (delimiter == "") { + res += std::move(line); + } else { + res += delimiter + std::move(line); + } + } + // Strip out extra null character at the end of the string. + if (!res.empty() && res[res.size() - 1] == 0) { + res.pop_back(); + } + // Replace all nulls with spaces. Sometimes the command line has + // null to separate arguments and others it has spaces. We just make them all spaces + // and leave it to upstream code to tokenize properly. + std::replace(res.begin(), res.end(), static_cast(0), ' '); + return res; } std::string ProcParser::GetPIDCmdline(uint32_t pid) const { - return ReadPIDFile(pid, "cmdline", ""); + return ReadPIDFile(pid, "cmdline", ""); } std::string ProcParser::GetPIDComm(uint32_t pid) const { - return ReadPIDFile(pid, "comm", ""); + return ReadPIDFile(pid, "comm", ""); } std::string ProcParser::GetPIDEnviron(uint32_t pid) const { - return ReadPIDFile(pid, "environ", ""); + return ReadPIDFile(pid, "environ", ""); } // Helper functions to split strings std::vector ProcParser::split(const std::string& str, char delimiter) const { - std::vector tokens; - std::stringstream ss(str); - std::string token; - while (std::getline(ss, token, delimiter)) { - tokens.push_back(token); - } - return tokens; + std::vector tokens; + std::stringstream ss(str); + std::string token; + while (std::getline(ss, token, delimiter)) { + tokens.push_back(token); + } + return tokens; } std::tuple ProcParser::ProcsContainerIdOffset(const std::string& subdir) const { - size_t p = subdir.rfind(':') + 1; - std::vector fields = split(subdir, ':'); - std::string idStr = fields.back(); - size_t off = idStr.rfind('-') + 1; - std::vector s = split(idStr, '-'); - return {s.back(), static_cast(off + p)}; + size_t p = subdir.rfind(':') + 1; + std::vector fields = split(subdir, ':'); + std::string idStr = fields.back(); + size_t off = idStr.rfind('-') + 1; + std::vector s = split(idStr, '-'); + return {s.back(), static_cast(off + p)}; } -std::tuple ProcParser::LookupContainerId(const std::string& cgroup, bool bpfSource, bool walkParent) const { - bool idTruncated = false; - std::vector subDirs = split(cgroup, '/'); - std::string subdir = subDirs.back(); - - if (subdir.find("syscont-cgroup-root") != std::string::npos) { - if (subDirs.size() > 4) { - subdir = subDirs[4]; - walkParent = false; +std::tuple +ProcParser::LookupContainerId(const std::string& cgroup, bool bpfSource, bool walkParent) const { + bool idTruncated = false; + std::vector subDirs = split(cgroup, '/'); + std::string subdir = subDirs.back(); + + if (subdir.find("syscont-cgroup-root") != std::string::npos) { + if (subDirs.size() > 4) { + subdir = subDirs[4]; + walkParent = false; + } } - } - if (bpfSource && subdir.size() >= DOCKER_ID_LENGTH - 1) { - idTruncated = true; - } + if (bpfSource && subdir.size() >= DOCKER_ID_LENGTH - 1) { + idTruncated = true; + } - auto [container, i] = ProcsContainerIdOffset(subdir); + auto [container, i] = ProcsContainerIdOffset(subdir); - if (container.size() >= ContainerIdLength || (idTruncated && container.size() >= BpfContainerIdLength)) { - return {container.substr(0, BpfContainerIdLength), i}; - } + if (container.size() >= ContainerIdLength || (idTruncated && container.size() >= BpfContainerIdLength)) { + return {container.substr(0, BpfContainerIdLength), i}; + } - if (cgroup.find("libpod") != std::string::npos && container == "container") { - walkParent = true; - } + if (cgroup.find("libpod") != std::string::npos && container == "container") { + walkParent = true; + } - if (!walkParent) { - return {"", 0}; - } + if (!walkParent) { + return {"", 0}; + } - for (int j = subDirs.size() - 2; j > 1; --j) { - auto [container, i] = ProcsContainerIdOffset(subDirs[j]); - if (container.size() == ContainerIdLength || (container.size() > ContainerIdLength && container.find("scope") != std::string::npos)) { - return {container.substr(0, BpfContainerIdLength), i}; + for (int j = subDirs.size() - 2; j > 1; --j) { + auto [container, i] = ProcsContainerIdOffset(subDirs[j]); + if (container.size() == ContainerIdLength + || (container.size() > ContainerIdLength && container.find("scope") != std::string::npos)) { + return {container.substr(0, BpfContainerIdLength), i}; + } } - } - return {"", 0}; + return {"", 0}; } std::tuple ProcParser::ProcsFindDockerId(const std::string& cgroups) const { - std::vector cgrpPaths = split(cgroups, '\n'); - for (const auto& s : cgrpPaths) { - if (s.find("pods") != std::string::npos || s.find("docker") != std::string::npos || s.find("libpod") != std::string::npos) { - auto [container, i] = LookupContainerId(s, false, false); - if (!container.empty()) { - LOG_DEBUG(sLogger, ("[ProcsFindDockerId] containerid", container)); - return {container, i}; - } - } - } - return {"", 0}; + std::vector cgrpPaths = split(cgroups, '\n'); + for (const auto& s : cgrpPaths) { + if (s.find("pods") != std::string::npos || s.find("docker") != std::string::npos + || s.find("libpod") != std::string::npos) { + auto [container, i] = LookupContainerId(s, false, false); + if (!container.empty()) { + LOG_DEBUG(sLogger, ("[ProcsFindDockerId] containerid", container)); + return {container, i}; + } + } + } + return {"", 0}; } std::string ProcParser::GetPIDDockerId(uint32_t pid) const { - std::string cgroups = ReadPIDFile(pid, "cgroup", "\n"); - auto [dockerId, offset] = ProcsFindDockerId(cgroups); - LOG_DEBUG(sLogger, ("[GetPIDDockerId] failed, pid:", pid) ("containerid", dockerId)); - return dockerId; + std::string cgroups = ReadPIDFile(pid, "cgroup", "\n"); + auto [dockerId, offset] = ProcsFindDockerId(cgroups); + LOG_DEBUG(sLogger, ("[GetPIDDockerId] failed, pid:", pid)("containerid", dockerId)); + return dockerId; } std::string ProcParser::GetPIDExePath(uint32_t pid) const { - return ReadPIDLink(pid, "exe"); + return ReadPIDLink(pid, "exe"); } -std::pair ProcParser::GetPIDCWD(uint32_t pid) const { - - ApiEventFlag flags = ApiEventFlag::Unknown; - if (pid == 0) { - return {"", static_cast(flags)}; - } - - try { - std::string cwd = ReadPIDLink(pid, "cwd"); - if (cwd == "/") { - flags |= ApiEventFlag::RootCWD; +std::pair ProcParser::GetPIDCWD(uint32_t pid) const { + ApiEventFlag flags = ApiEventFlag::Unknown; + if (pid == 0) { + return {"", static_cast(flags)}; } - return {cwd, static_cast(flags)}; - } catch(const std::filesystem::filesystem_error&) { - flags |= (ApiEventFlag::RootCWD | ApiEventFlag::ErrorCWD); - return {"", static_cast(flags)}; - } + try { + std::string cwd = ReadPIDLink(pid, "cwd"); + if (cwd == "/") { + flags |= ApiEventFlag::RootCWD; + } + return {cwd, static_cast(flags)}; + } catch (const std::filesystem::filesystem_error&) { + flags |= (ApiEventFlag::RootCWD | ApiEventFlag::ErrorCWD); + return {"", static_cast(flags)}; + } } std::string ProcParser::GetUserNameByUid(uid_t uid) { @@ -274,243 +276,244 @@ std::string ProcParser::GetUserNameByUid(uid_t uid) { } std::tuple ProcParser::GetPIDCaps(uint32_t origin_pid) const { - uint32_t pid = 0; - uint64_t permitted = 0; - uint64_t effective = 0; - uint64_t inheritable = 0; - - auto getValue64Hex = [](const std::string& line) -> std::tuple { - std::istringstream stream(line); - std::string field; - uint64_t value; - - while (stream >> field); - try { - value = std::stoull(field, nullptr, 16); - } catch (const std::invalid_argument&) { - return {0, "Invalid argument in line: " + line}; - } catch (const std::out_of_range&) { - return {0, "Out of range in line: " + line}; + uint32_t pid = 0; + uint64_t permitted = 0; + uint64_t effective = 0; + uint64_t inheritable = 0; + + auto getValue64Hex = [](const std::string& line) -> std::tuple { + std::istringstream stream(line); + std::string field; + uint64_t value; + + while (stream >> field) + ; + try { + value = std::stoull(field, nullptr, 16); + } catch (const std::invalid_argument&) { + return {0, "Invalid argument in line: " + line}; + } catch (const std::out_of_range&) { + return {0, "Out of range in line: " + line}; + } + return {value, ""}; + }; + + auto getValue32Int = [](const std::string& line) -> std::tuple { + std::istringstream stream(line); + std::string field; + uint32_t value; + + while (stream >> field) + ; + try { + value = std::stoul(field); + } catch (const std::invalid_argument&) { + return {0, "Invalid argument in line: " + line}; + } catch (const std::out_of_range&) { + return {0, "Out of range in line: " + line}; + } + return {value, ""}; + }; + + std::string filename = proc_path_ + "/" + std::to_string(origin_pid) + "/status"; + std::ifstream file(filename); + if (!file.is_open()) { + LOG_WARNING(sLogger, ("ReadFile failed", filename)); + return {0, 0, 0, 0}; } - return {value, ""}; - }; - auto getValue32Int = [](const std::string& line) -> std::tuple { - std::istringstream stream(line); - std::string field; - uint32_t value; + std::string line; + while (std::getline(file, line)) { + std::string err; + if (line.find("NStgid:") != std::string::npos) { + std::tie(pid, err) = getValue32Int(line); + } else if (line.find("CapPrm:") != std::string::npos) { + std::tie(permitted, err) = getValue64Hex(line); + } else if (line.find("CapEff:") != std::string::npos) { + std::tie(effective, err) = getValue64Hex(line); + } else if (line.find("CapInh:") != std::string::npos) { + std::tie(inheritable, err) = getValue64Hex(line); + } + if (!err.empty()) { + LOG_WARNING(sLogger, ("ReadFile error, filename", filename)("line", line)); + } + } - while (stream >> field); - try { - value = std::stoul(field); - } catch (const std::invalid_argument&) { - return {0, "Invalid argument in line: " + line}; - } catch (const std::out_of_range&) { - return {0, "Out of range in line: " + line}; - } - return {value, ""}; - }; - - std::string filename = proc_path_ + "/" + std::to_string(origin_pid) + "/status"; - std::ifstream file(filename); - if (!file.is_open()) { - LOG_WARNING(sLogger, ("ReadFile failed", filename)); - return {0, 0, 0, 0}; - } - - std::string line; - while (std::getline(file, line)) { - std::string err; - if (line.find("NStgid:") != std::string::npos) { - std::tie(pid, err) = getValue32Int(line); - } else if (line.find("CapPrm:") != std::string::npos) { - std::tie(permitted, err) = getValue64Hex(line); - } else if (line.find("CapEff:") != std::string::npos) { - std::tie(effective, err) = getValue64Hex(line); - } else if (line.find("CapInh:") != std::string::npos) { - std::tie(inheritable, err) = getValue64Hex(line); - } - if (!err.empty()) { - LOG_WARNING(sLogger, ("ReadFile error, filename", filename) ("line", line)); - } - } - - return {pid, permitted, effective, inheritable}; + return {pid, permitted, effective, inheritable}; } -uint64_t ProcParser::GetStatsKtime(std::vector &proc_stat) const { - if (proc_stat.size() <= 21) { - throw std::out_of_range("Index 21 is out of range for the input vector"); - } +uint64_t ProcParser::GetStatsKtime(std::vector& proc_stat) const { + if (proc_stat.size() <= 21) { + throw std::out_of_range("Index 21 is out of range for the input vector"); + } - try { - uint64_t ktime = std::stoull(proc_stat[21]); - return ktime * (nanoPerSeconds / clktck); - } catch (const std::invalid_argument& e) { - LOG_WARNING(sLogger, ("Invalid argument, e", e.what())); - return 0; - } catch (const std::out_of_range& e) { - LOG_WARNING(sLogger, ("Out of range, proc stat size", proc_stat.size())); - return 0; - } + try { + uint64_t ktime = std::stoull(proc_stat[21]); + return ktime * (nanoPerSeconds / clktck); + } catch (const std::invalid_argument& e) { + LOG_WARNING(sLogger, ("Invalid argument, e", e.what())); + return 0; + } catch (const std::out_of_range& e) { + LOG_WARNING(sLogger, ("Out of range, proc stat size", proc_stat.size())); + return 0; + } } -uint32_t ProcParser::GetPIDNsInode(uint32_t pid, const std::string &ns_str) const { - std::string pidStr = std::to_string(pid); - std::filesystem::path netns = std::filesystem::path(proc_path_) / pidStr / "ns" / ns_str; +uint32_t ProcParser::GetPIDNsInode(uint32_t pid, const std::string& ns_str) const { + std::string pidStr = std::to_string(pid); + std::filesystem::path netns = std::filesystem::path(proc_path_) / pidStr / "ns" / ns_str; - std::error_code ec; - std::string netStr = std::filesystem::read_symlink(netns, ec).string(); - if (ec) { - LOG_WARNING(sLogger, ("namespace", netns)("error" , ec.message())); - return 0; - } + std::error_code ec; + std::string netStr = std::filesystem::read_symlink(netns, ec).string(); + if (ec) { + LOG_WARNING(sLogger, ("namespace", netns)("error", ec.message())); + return 0; + } - std::vector fields = split(netStr, ':'); - if (fields.size() < 2) { - LOG_WARNING(sLogger, ("parsing namespace fields less than 2, net str ", netStr) ("netns", netns)); - return 0; - } - - std::string inode = fields[1]; - inode = inode.substr(1, inode.size() - 2); // Remove [ and ] - uint64_t inodeEntry; - try { - inodeEntry = std::stoull(inode); - } catch (const std::invalid_argument& e) { - LOG_WARNING(sLogger, ("Invalid argument, e", e.what())); - return 0; - } catch (const std::out_of_range& e) { - LOG_WARNING(sLogger, ("Out of range:, e", e.what())); - return 0; - } + std::vector fields = split(netStr, ':'); + if (fields.size() < 2) { + LOG_WARNING(sLogger, ("parsing namespace fields less than 2, net str ", netStr)("netns", netns)); + return 0; + } - return static_cast(inodeEntry); -} + std::string inode = fields[1]; + inode = inode.substr(1, inode.size() - 2); // Remove [ and ] + uint64_t inodeEntry; + try { + inodeEntry = std::stoull(inode); + } catch (const std::invalid_argument& e) { + LOG_WARNING(sLogger, ("Invalid argument, e", e.what())); + return 0; + } catch (const std::out_of_range& e) { + LOG_WARNING(sLogger, ("Out of range:, e", e.what())); + return 0; + } -int ProcParser::FillStatus(uint32_t pid, std::shared_ptr status) const { + return static_cast(inodeEntry); +} - const auto path = ProcPidPath(pid, "status"); +int ProcParser::FillStatus(uint32_t pid, std::shared_ptr status) const { + const auto path = ProcPidPath(pid, "status"); - std::ifstream f(path); + std::ifstream f(path); - if (!f.is_open()) { - LOG_WARNING(sLogger, ("open failed, path", path)); - return -1; - } - - std::string line; - while (std::getline(f, line)) { - std::istringstream iss(line); - std::vector fields; - std::string field; - while (iss >> field) { - fields.push_back(field); - } - if (fields.size() < 2) continue; - if (fields[0] == "Uid:") { - if (fields.size() != 5) { - LOG_WARNING(sLogger, ("Reading Uid failed: malformed input, path", path)); - return -1; - } - status->uids = {fields[1], fields[2], fields[3], fields[4]}; - } - if (fields[0] == "Gid:") { - if (fields.size() != 5) { - LOG_WARNING(sLogger, ("Reading Gid failed: malformed input, path", path)); + if (!f.is_open()) { + LOG_WARNING(sLogger, ("open failed, path", path)); return -1; - } - status->gids = {fields[1], fields[2], fields[3], fields[4]}; } - if (!status->uids.empty() && !status->gids.empty()) { - break; + + std::string line; + while (std::getline(f, line)) { + std::istringstream iss(line); + std::vector fields; + std::string field; + while (iss >> field) { + fields.push_back(field); + } + if (fields.size() < 2) + continue; + if (fields[0] == "Uid:") { + if (fields.size() != 5) { + LOG_WARNING(sLogger, ("Reading Uid failed: malformed input, path", path)); + return -1; + } + status->uids = {fields[1], fields[2], fields[3], fields[4]}; + } + if (fields[0] == "Gid:") { + if (fields.size() != 5) { + LOG_WARNING(sLogger, ("Reading Gid failed: malformed input, path", path)); + return -1; + } + status->gids = {fields[1], fields[2], fields[3], fields[4]}; + } + if (!status->uids.empty() && !status->gids.empty()) { + break; + } } - } - return 0; + return 0; } -int ProcParser::FillLoginUid(uint32_t pid, std::shared_ptr status) const { - - try { - std::string login_uid = ReadPIDFile(pid, "loginuid", ""); - status->login_uid = login_uid; - } catch (std::runtime_error &error) { - return -1; - } - return 0; +int ProcParser::FillLoginUid(uint32_t pid, std::shared_ptr status) const { + try { + std::string login_uid = ReadPIDFile(pid, "loginuid", ""); + status->login_uid = login_uid; + } catch (std::runtime_error& error) { + return -1; + } + return 0; } // TODO @qianlu.kk std::shared_ptr ProcParser::GetStatus(uint32_t pid) const { - auto status = std::make_shared(); + auto status = std::make_shared(); - if (FillStatus(pid, status) != 0 || FillLoginUid(pid, status) != 0) { - return nullptr; - } - return status; + if (FillStatus(pid, status) != 0 || FillLoginUid(pid, status) != 0) { + return nullptr; + } + return status; } -std::tuple ProcParser::ProcsFilename(const std::string &args) { - std::string filename = args; - std::string cmds = ""; - size_t idx = args.find("\0"); - - if (idx == std::string::npos) { - filename = args; - } else { - cmds = args.substr(idx); - filename = args.substr(0, idx); -// if (idx == 0) { -// filename = ""; -// } else { -// -// } - } +std::tuple ProcParser::ProcsFilename(const std::string& args) { + std::string filename = args; + std::string cmds = ""; + size_t idx = args.find('\0'); - return std::make_tuple(cmds, filename); -} + if (idx == std::string::npos) { + filename = args; + } else { + cmds = args.substr(idx); + filename = args.substr(0, idx); + // if (idx == 0) { + // filename = ""; + // } else { + // + // } + } -std::vector ProcParser::GetProcStatStrings(uint32_t pid) const { - std::string path = proc_path_ + "/" + std::to_string(pid) + "/stat"; - std::ifstream file(path); - if (!file.is_open()) { - throw std::runtime_error("ReadFile: " + path + "/stat error"); - } - - std::stringstream buffer; - buffer << file.rdbuf(); - std::string statline = buffer.str(); - - std::vector output; - size_t oldIndex = statline.length(); - size_t index = statline.find_last_of(' '); - - // Build list of strings in reverse order - while (index != std::string::npos) { - output.push_back(statline.substr(index + 1, oldIndex - index - 1)); - if (statline[index - 1] == ')') { - break; - } - oldIndex = index; - index = statline.find_last_of(' ', oldIndex - 1); - } - - if (index == std::string::npos) { - output.push_back(statline.substr(0, oldIndex)); - } else { - size_t commIndex = statline.find_first_of(' '); - output.push_back(statline.substr(commIndex + 1, index - commIndex - 1)); - output.push_back(statline.substr(0, commIndex)); - } - - // Reverse the array - std::reverse(output.begin(), output.end()); - - return output; + return std::make_tuple(cmds, filename); } + +std::vector ProcParser::GetProcStatStrings(uint32_t pid) const { + std::string path = proc_path_ + "/" + std::to_string(pid) + "/stat"; + std::ifstream file(path); + if (!file.is_open()) { + throw std::runtime_error("ReadFile: " + path + "/stat error"); + } + + std::stringstream buffer; + buffer << file.rdbuf(); + std::string statline = buffer.str(); + + std::vector output; + size_t oldIndex = statline.length(); + size_t index = statline.find_last_of(' '); + + // Build list of strings in reverse order + while (index != std::string::npos) { + output.push_back(statline.substr(index + 1, oldIndex - index - 1)); + if (statline[index - 1] == ')') { + break; + } + oldIndex = index; + index = statline.find_last_of(' ', oldIndex - 1); + } + + if (index == std::string::npos) { + output.push_back(statline.substr(0, oldIndex)); + } else { + size_t commIndex = statline.find_first_of(' '); + output.push_back(statline.substr(commIndex + 1, index - commIndex - 1)); + output.push_back(statline.substr(0, commIndex)); + } + + // Reverse the array + std::reverse(output.begin(), output.end()); + + return output; } +} // namespace logtail -//ProcParser::GetPIDStartTimeTicks(uint32_t pid) const { -//return ::px::system::GetPIDStartTimeTicks(ProcPidPath(pid)); -//} \ No newline at end of file +// ProcParser::GetPIDStartTimeTicks(uint32_t pid) const { +// return ::px::system::GetPIDStartTimeTicks(ProcPidPath(pid)); +// } diff --git a/core/ebpf/plugin/network_observer/ConnTrackerManager.cpp b/core/ebpf/plugin/network_observer/ConnTrackerManager.cpp index 6623d656c8..352f201b0a 100644 --- a/core/ebpf/plugin/network_observer/ConnTrackerManager.cpp +++ b/core/ebpf/plugin/network_observer/ConnTrackerManager.cpp @@ -179,28 +179,6 @@ void ConnTrackerManager::AcceptNetCtrlEvent(struct conn_ctrl_event_t* event) { conn_tracker->RecordActive(); } -void ConnTrackerManager::Report() { - // int count = 0; - // while(flag_) { - // int n = 0; - // for (auto it = conn_trackers_.begin(); it != conn_trackers_.end(); ++it) { - // LOG(INFO) << "[Report] Key: " << it->first << " Value: " << it->second ; - // ConnId id = it->first; - // std::shared_ptr ct = it->second; - - // aggregate - // TODO @qianlu.kk wait!!!! - // auto record = ct->GetConnStatsGauge(); - // Aggregator::GetInstance().Aggregate(record); - // n++; - // } - // count++; - // LOG(INFO) << "[Report] " << count << " push " << n << " conn stats to aggregator ..." ; - // std::this_thread::sleep_for(std::chrono::seconds(report_interval_sec_)); - // } - return; -} - void ConnTrackerManager::IterationsInternal(int count_) { std::chrono::time_point now = std::chrono::steady_clock::now(); auto now_ts = std::chrono::duration_cast(now.time_since_epoch()).count(); @@ -369,5 +347,6 @@ void ConnTrackerManager::Start() { void ConnTrackerManager::Stop() { flag_ = false; } + } // namespace ebpf } // namespace logtail diff --git a/core/ebpf/plugin/network_observer/NetworkObserverManager.cpp b/core/ebpf/plugin/network_observer/NetworkObserverManager.cpp index 621f3e27e0..5d810085b9 100644 --- a/core/ebpf/plugin/network_observer/NetworkObserverManager.cpp +++ b/core/ebpf/plugin/network_observer/NetworkObserverManager.cpp @@ -550,11 +550,6 @@ int NetworkObserverManager::Init(const std::variantmEnableSpan; mEnableMetric = opt->mEnableMetric; - // TODO @qianlu.kk - if (StartAggregator()) { - LOG_ERROR(sLogger, ("failed to start aggregator", "")); - } - // diff opt if (mPreviousOpt) { CompareAndUpdate("mDisableConnStats", @@ -647,13 +642,6 @@ int NetworkObserverManager::Init(const std::variant& protocols); - int StartAggregator(); - int StopAggregator(); - std::shared_ptr mConnTrackerMgr; // TODO @qianlu.kk modify T for abstract event diff --git a/core/unittest/common/CMakeLists.txt b/core/unittest/common/CMakeLists.txt index 288129372f..1cedfed60e 100644 --- a/core/unittest/common/CMakeLists.txt +++ b/core/unittest/common/CMakeLists.txt @@ -57,6 +57,15 @@ target_link_libraries(timer_unittest ${UT_BASE_TARGET}) add_executable(curl_unittest http/CurlUnittest.cpp) target_link_libraries(curl_unittest ${UT_BASE_TARGET}) +add_executable(proc_parser_unittest ProcParserUnittest.cpp) +target_link_libraries(proc_parser_unittest ${UT_BASE_TARGET}) + +add_executable(capability_util_unittest CapabilityUtilUnittest.cpp) +target_link_libraries(capability_util_unittest ${UT_BASE_TARGET}) + +add_executable(network_util_unittest NetworkUtilUnittest.cpp) +target_link_libraries(network_util_unittest ${UT_BASE_TARGET}) + include(GoogleTest) gtest_discover_tests(common_simple_utils_unittest) gtest_discover_tests(common_logfileoperator_unittest) @@ -69,3 +78,7 @@ gtest_discover_tests(safe_queue_unittest) gtest_discover_tests(http_request_timer_event_unittest) gtest_discover_tests(timer_unittest) gtest_discover_tests(curl_unittest) +gtest_discover_tests(proc_parser_unittest) +gtest_discover_tests(proc_parser_unittest) +gtest_discover_tests(capability_util_unittest) +gtest_discover_tests(network_util_unittest) diff --git a/core/unittest/common/CapabilityUtilUnittest.cpp b/core/unittest/common/CapabilityUtilUnittest.cpp new file mode 100644 index 0000000000..c80232b79c --- /dev/null +++ b/core/unittest/common/CapabilityUtilUnittest.cpp @@ -0,0 +1,82 @@ +// Copyright 2023 iLogtail Authors +// +// 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 "common/CapabilityUtil.h" +#include "unittest/Unittest.h" + +namespace logtail { + +class CapabilityUtilUnittest : public ::testing::Test { +public: + void TestGetCapability(); + void TestGetCapabilities(); + void TestInvalidCapability(); +}; + +void CapabilityUtilUnittest::TestGetCapability() { + // Test some common capabilities + APSARA_TEST_STREQ_DESC(GetCapability(0).c_str(), "CAP_CHOWN", "CAP_CHOWN should match"); + APSARA_TEST_STREQ_DESC(GetCapability(1).c_str(), "DAC_OVERRIDE", "DAC_OVERRIDE should match"); + APSARA_TEST_STREQ_DESC(GetCapability(2).c_str(), "CAP_DAC_READ_SEARCH", "CAP_DAC_READ_SEARCH should match"); + APSARA_TEST_STREQ_DESC(GetCapability(7).c_str(), "CAP_SETUID", "CAP_SETUID should match"); + APSARA_TEST_STREQ_DESC(GetCapability(21).c_str(), "CAP_SYS_ADMIN", "CAP_SYS_ADMIN should match"); +} + +void CapabilityUtilUnittest::TestGetCapabilities() { + // Test single capability + uint64_t singleCap = 1ULL << 0; // CAP_CHOWN + APSARA_TEST_STREQ_DESC(GetCapabilities(singleCap).c_str(), "CAP_CHOWN", "Single capability should match"); + + // Test multiple capabilities + uint64_t multipleCaps = (1ULL << 0) | (1ULL << 1) | (1ULL << 7); // CAP_CHOWN, DAC_OVERRIDE, CAP_SETUID + std::string result = GetCapabilities(multipleCaps); + APSARA_TEST_TRUE(result.find("CAP_CHOWN") != std::string::npos); + APSARA_TEST_TRUE(result.find("DAC_OVERRIDE") != std::string::npos); + APSARA_TEST_TRUE(result.find("CAP_SETUID") != std::string::npos); + + // Test no capabilities + APSARA_TEST_STREQ_DESC(GetCapabilities(0).c_str(), "", "No capabilities should return empty string"); + + // Test all capabilities + uint64_t allCaps = ~0ULL; + result = GetCapabilities(allCaps); + APSARA_TEST_TRUE(result.find("CAP_CHOWN") != std::string::npos); + APSARA_TEST_TRUE(result.find("CAP_SYS_ADMIN") != std::string::npos); + APSARA_TEST_TRUE(result.find("CAP_CHECKPOINT_RESTORE") != std::string::npos); +} + +void CapabilityUtilUnittest::TestInvalidCapability() { + // Test invalid capability value + try { + GetCapability(-1); + APSARA_TEST_TRUE(false); // Should not reach here + } catch (const std::invalid_argument& e) { + APSARA_TEST_TRUE(true); + } + + try { + GetCapability(100); + APSARA_TEST_TRUE(false); // Should not reach here + } catch (const std::invalid_argument& e) { + APSARA_TEST_TRUE(true); + } +} + +UNIT_TEST_CASE(CapabilityUtilUnittest, TestGetCapability); +UNIT_TEST_CASE(CapabilityUtilUnittest, TestGetCapabilities); +UNIT_TEST_CASE(CapabilityUtilUnittest, TestInvalidCapability); + +} // namespace logtail + +UNIT_TEST_MAIN diff --git a/core/unittest/common/NetworkUtilUnittest.cpp b/core/unittest/common/NetworkUtilUnittest.cpp new file mode 100644 index 0000000000..3427162ad1 --- /dev/null +++ b/core/unittest/common/NetworkUtilUnittest.cpp @@ -0,0 +1,89 @@ +// Copyright 2023 iLogtail Authors +// +// 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 "common/NetworkUtil.h" +#include "unittest/Unittest.h" + +#if defined(__linux__) +#include +#endif + +namespace logtail { + +class NetworkUtilUnittest : public ::testing::Test { +public: + void TestGetAddrString(); + void TestGetFamilyString(); + void TestGetProtocolString(); + void TestGetStateString(); +}; + +void NetworkUtilUnittest::TestGetAddrString() { +#if defined(__linux__) + // Test IP address conversion + // 127.0.0.1 in network byte order + uint32_t localhost = 0x0100007F; + APSARA_TEST_STREQ_DESC(GetAddrString(localhost).c_str(), "127.0.0.1", "Localhost IP should match"); + + // 192.168.1.1 in network byte order + uint32_t localIP = 0x0101A8C0; + APSARA_TEST_STREQ_DESC(GetAddrString(localIP).c_str(), "192.168.1.1", "Local IP should match"); + + // 8.8.8.8 in network byte order + uint32_t googleDNS = 0x08080808; + APSARA_TEST_STREQ_DESC(GetAddrString(googleDNS).c_str(), "8.8.8.8", "Google DNS IP should match"); +#endif +} + +void NetworkUtilUnittest::TestGetFamilyString() { +#if defined(__linux__) + // Test common address families + APSARA_TEST_STREQ_DESC(GetFamilyString(AF_INET).c_str(), "AF_INET", "IPv4 family should match"); + APSARA_TEST_STREQ_DESC(GetFamilyString(AF_INET6).c_str(), "AF_INET6", "IPv6 family should match"); + APSARA_TEST_STREQ_DESC(GetFamilyString(AF_UNIX).c_str(), "AF_UNIX", "Unix domain socket family should match"); + + // Test unknown family + APSARA_TEST_TRUE(GetFamilyString(9999).find_first_not_of("0123456789") == std::string::npos); +#endif +} + +void NetworkUtilUnittest::TestGetProtocolString() { + // Test common protocols + APSARA_TEST_STREQ_DESC(GetProtocolString(1).c_str(), "ICMP", "ICMP protocol should match"); + APSARA_TEST_STREQ_DESC(GetProtocolString(6).c_str(), "TCP", "TCP protocol should match"); + APSARA_TEST_STREQ_DESC(GetProtocolString(17).c_str(), "UDP", "UDP protocol should match"); + APSARA_TEST_STREQ_DESC(GetProtocolString(89).c_str(), "OSPF", "OSPF protocol should match"); + + // Test unknown protocol + APSARA_TEST_STREQ_DESC(GetProtocolString(999).c_str(), "Unknown", "Unknown protocol should return 'Unknown'"); +} + +void NetworkUtilUnittest::TestGetStateString() { + // Test TCP states + APSARA_TEST_STREQ_DESC(GetStateString(1).c_str(), "TCP_ESTABLISHED", "TCP ESTABLISHED state should match"); + APSARA_TEST_STREQ_DESC(GetStateString(2).c_str(), "TCP_SYN_SENT", "TCP SYN_SENT state should match"); + APSARA_TEST_STREQ_DESC(GetStateString(10).c_str(), "TCP_LISTEN", "TCP LISTEN state should match"); + + // Test invalid state + APSARA_TEST_STREQ_DESC(GetStateString(999).c_str(), "INVALID_STATE", "Invalid state should return 'INVALID_STATE'"); +} + +UNIT_TEST_CASE(NetworkUtilUnittest, TestGetAddrString); +UNIT_TEST_CASE(NetworkUtilUnittest, TestGetFamilyString); +UNIT_TEST_CASE(NetworkUtilUnittest, TestGetProtocolString); +UNIT_TEST_CASE(NetworkUtilUnittest, TestGetStateString); + +} // namespace logtail + +UNIT_TEST_MAIN diff --git a/core/unittest/common/ProcParserUnittest.cpp b/core/unittest/common/ProcParserUnittest.cpp new file mode 100644 index 0000000000..d4d04d872f --- /dev/null +++ b/core/unittest/common/ProcParserUnittest.cpp @@ -0,0 +1,211 @@ +// Copyright 2023 iLogtail Authors +// +// 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 +#include +#include +#include + +#include "common/ProcParser.h" +#include "unittest/Unittest.h" + +namespace logtail { + +class ProcParserUnittest : public ::testing::Test { +public: + void TestGetPIDCmdline(); + void TestGetPIDComm(); + void TestGetPIDEnviron(); + void TestGetPIDCWD(); + void TestGetPIDDockerId(); + void TestGetPIDExePath(); + void TestGetStatus(); + void TestGetPIDCaps(); + void TestGetPIDNsInode(); + void TestProcsFilename(); + +protected: + void SetUp() override { + mTestRoot = std::filesystem::path(GetProcessExecutionDir()) / "ProcParserUnittestDir"; + mProcDir = mTestRoot / "proc"; + std::filesystem::create_directories(mProcDir); + mParser = std::make_unique(mTestRoot.string()); + } + + void TearDown() override { std::filesystem::remove_all(mTestRoot); } + + void WriteStringWithNulls(const std::filesystem::path& path, const char* data, size_t size) { + std::ofstream ofs(path, std::ios::binary); + ofs.write(data, size); + } + + void CreateProcTestFiles(int pid) { + auto pidDir = mProcDir / std::to_string(pid); + std::filesystem::create_directories(pidDir); + + // Create cmdline file with null separators + const char cmdline[] = {'t', 'e', 's', 't', '\0', 'p', 'r', 'o', 'g', 'r', 'a', + 'm', '\0', 'a', 'r', 'g', '1', '\0', 'a', 'r', 'g', '2'}; + WriteStringWithNulls(pidDir / "cmdline", cmdline, sizeof(cmdline)); + + // Create comm file + std::ofstream(pidDir / "comm") << "test_program"; + + // Create environ file with null separators + const char environ[] + = {'P', 'A', 'T', 'H', '=', '/', 'u', 's', 'r', '/', 'b', 'i', 'n', '\0', 'U', 'S', 'E', 'R', + '=', 'r', 'o', 'o', 't', '\0', 'H', 'O', 'M', 'E', '=', '/', 'r', 'o', 'o', 't', '\0'}; + WriteStringWithNulls(pidDir / "environ", environ, sizeof(environ)); + + // Create status file + std::ofstream status(pidDir / "status"); + status << "Name: test_program\n" + << "Uid: 1000 1000 1000 1000\n" + << "Gid: 1000 1000 1000 1000\n"; + status.close(); + + // Create loginuid file + std::ofstream(pidDir / "loginuid") << "1000"; + + // Create cgroup file + std::ofstream(pidDir / "cgroup") + << "0::/docker/1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"; + + // Create exe symlink + std::filesystem::create_directories(mTestRoot / "usr" / "bin"); + std::ofstream(mTestRoot / "usr" / "bin" / "test_program") << "test program binary"; + std::filesystem::create_symlink(mTestRoot / "usr" / "bin" / "test_program", pidDir / "exe"); + + // Create cwd symlink + std::filesystem::create_directories(mTestRoot / "home" / "user"); + std::filesystem::create_symlink(mTestRoot / "home" / "user", pidDir / "cwd"); + + // Create ns directory and net symlink + std::filesystem::create_directories(pidDir / "ns"); + std::filesystem::create_symlink("net:[4026531992]", pidDir / "ns" / "net"); + } + +private: + std::filesystem::path mTestRoot; + std::filesystem::path mProcDir; + std::unique_ptr mParser; +}; + +void ProcParserUnittest::TestGetPIDCmdline() { + const int testPid = 12345; + CreateProcTestFiles(testPid); + + std::string cmdline = mParser->GetPIDCmdline(testPid); + APSARA_TEST_STREQ_DESC(cmdline.c_str(), "test program arg1 arg2", "Cmdline should match"); +} + +void ProcParserUnittest::TestGetPIDComm() { + const int testPid = 12345; + CreateProcTestFiles(testPid); + + std::string comm = mParser->GetPIDComm(testPid); + APSARA_TEST_STREQ_DESC(comm.c_str(), "test_program", "Comm should match"); +} + +void ProcParserUnittest::TestGetPIDEnviron() { + const int testPid = 12345; + CreateProcTestFiles(testPid); + + std::string environ = mParser->GetPIDEnviron(testPid); + APSARA_TEST_TRUE(environ.find("PATH=/usr/bin") != std::string::npos); + APSARA_TEST_TRUE(environ.find("USER=root") != std::string::npos); + APSARA_TEST_TRUE(environ.find("HOME=/root") != std::string::npos); +} + +void ProcParserUnittest::TestGetPIDCWD() { + const int testPid = 12345; + CreateProcTestFiles(testPid); + + auto [cwd, flags] = mParser->GetPIDCWD(testPid); + APSARA_TEST_TRUE(cwd.find("/home/user") != std::string::npos); + APSARA_TEST_EQUAL(flags & static_cast(ApiEventFlag::RootCWD), 0); +} + +void ProcParserUnittest::TestGetPIDDockerId() { + const int testPid = 12345; + CreateProcTestFiles(testPid); + + std::string dockerId = mParser->GetPIDDockerId(testPid); + APSARA_TEST_STREQ_DESC( + dockerId.c_str(), "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", "Docker ID should match"); +} + +void ProcParserUnittest::TestGetPIDExePath() { + const int testPid = 12345; + CreateProcTestFiles(testPid); + + std::string exePath = mParser->GetPIDExePath(testPid); + APSARA_TEST_TRUE(exePath.find("/usr/bin/test_program") != std::string::npos); +} + +void ProcParserUnittest::TestGetStatus() { + const int testPid = 12345; + CreateProcTestFiles(testPid); + + auto status = mParser->GetStatus(testPid); + APSARA_TEST_TRUE(status != nullptr); + + auto uids = status->GetUids(); + APSARA_TEST_EQUAL(uids.size(), 4); + for (const auto& uid : uids) { + APSARA_TEST_EQUAL(uid, 1000); + } + + auto gids = status->GetGids(); + APSARA_TEST_EQUAL(gids.size(), 4); + for (const auto& gid : gids) { + APSARA_TEST_EQUAL(gid, 1000); + } + + APSARA_TEST_EQUAL(status->GetLoginUid(), 1000); +} + +void ProcParserUnittest::TestGetPIDNsInode() { + const int testPid = 12345; + CreateProcTestFiles(testPid); + + uint32_t nsInode = mParser->GetPIDNsInode(testPid, "net"); + APSARA_TEST_EQUAL(nsInode, 4026531992); +} + +void ProcParserUnittest::TestProcsFilename() { + const char args[] = {'t', 'e', 's', 't', '\0', 'p', 'r', 'o', 'g', 'r', 'a', + 'm', '\0', 'a', 'r', 'g', '1', '\0', 'a', 'r', 'g', '2'}; + std::string argsStr(args, sizeof(args)); + auto [cmds, filename] = mParser->ProcsFilename(argsStr); + auto idx = argsStr.find('\0'); + auto fn = argsStr.substr(0, idx); + auto cmd = argsStr.substr(idx); + APSARA_TEST_STREQ_DESC(filename.c_str(), "test", "Filename should match"); + APSARA_TEST_TRUE(cmds.find("program") != std::string::npos); +} + +UNIT_TEST_CASE(ProcParserUnittest, TestGetPIDCmdline); +UNIT_TEST_CASE(ProcParserUnittest, TestGetPIDComm); +UNIT_TEST_CASE(ProcParserUnittest, TestGetPIDEnviron); +UNIT_TEST_CASE(ProcParserUnittest, TestGetPIDCWD); +UNIT_TEST_CASE(ProcParserUnittest, TestGetPIDDockerId); +UNIT_TEST_CASE(ProcParserUnittest, TestGetPIDExePath); +UNIT_TEST_CASE(ProcParserUnittest, TestGetStatus); +UNIT_TEST_CASE(ProcParserUnittest, TestGetPIDNsInode); +UNIT_TEST_CASE(ProcParserUnittest, TestProcsFilename); + +} // namespace logtail + +UNIT_TEST_MAIN diff --git a/core/unittest/common/timer/TimerUnittest.cpp b/core/unittest/common/timer/TimerUnittest.cpp index 67278167bb..035551dee4 100644 --- a/core/unittest/common/timer/TimerUnittest.cpp +++ b/core/unittest/common/timer/TimerUnittest.cpp @@ -12,7 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include + #include "common/timer/Timer.h" +#include "ebpf/type/PeriodicalEvent.h" #include "unittest/Unittest.h" using namespace std; @@ -31,6 +34,11 @@ struct TimerEventMock : public TimerEvent { class TimerUnittest : public ::testing::Test { public: void TestPushEvent(); + void TestPeriodicEvent(); + +private: + std::vector mVec; + int mStartUid; }; void TimerUnittest::TestPushEvent() { @@ -49,7 +57,33 @@ void TimerUnittest::TestPushEvent() { timer.mQueue.pop(); } +void TimerUnittest::TestPeriodicEvent() { + auto now = chrono::steady_clock::now(); + Timer timer; + timer.Init(); + std::unique_ptr event = std::make_unique( + 1, // interval second + [this](const std::chrono::steady_clock::time_point& execTime) { // handler + this->mVec.push_back(1); + return true; + }, + [this](int currentUid) { // validator + return currentUid == this->mStartUid; + }, + mStartUid); + + std::this_thread::sleep_for(std::chrono::seconds(5)); + // update start uid, invalid event schedule ... + mStartUid++; + std::this_thread::sleep_for(std::chrono::seconds(2)); + APSARA_TEST_TRUE(timer.mQueue.empty()); + APSARA_TEST_EQUAL(mVec.size(), 5); + timer.Stop(); +} + UNIT_TEST_CASE(TimerUnittest, TestPushEvent) +UNIT_TEST_CASE(TimerUnittest, TestPeriodicEvent) + } // namespace logtail