<commit_before>// Copyright (c) 2020 The Orbit Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ServiceUtils.h"

#include <absl/base/casts.h>
#include <absl/base/macros.h>
#include <absl/strings/match.h>
#include <absl/strings/numbers.h>
#include <absl/strings/str_cat.h>
#include <absl/strings/str_join.h>
#include <absl/strings/str_split.h>
#include <sys/uio.h>

#include <algorithm>
#include <filesystem>
#include <iosfwd>
#include <map>
#include <memory>
#include <numeric>
#include <outcome.hpp>
#include <set>
#include <string>
#include <string_view>
#include <system_error>
#include <utility>

#include "ElfUtils/ElfFile.h"
#include "OrbitBase/Logging.h"
#include "OrbitBase/ReadFileToString.h"
#include "OrbitBase/Result.h"
#include "absl/strings/str_format.h"
#include "module.pb.h"

namespace orbit_service::utils {

namespace fs = std::filesystem;

using ::orbit_elf_utils::ElfFile;
using orbit_grpc_protos::ModuleInfo;
using orbit_grpc_protos::TracepointInfo;

static const char* kLinuxTracingEvents = "/sys/kernel/debug/tracing/events/";

ErrorMessageOr<std::vector<orbit_grpc_protos::TracepointInfo>> ReadTracepoints() {
  std::vector<TracepointInfo> result;

  std::error_code error_code_category, error_code_name;
  std::error_code no_err;
  std::string error_category_message, error_name_message;

  for (const auto& category : fs::directory_iterator(kLinuxTracingEvents, error_code_category)) {
    if (fs::is_directory(category)) {
      for (const auto& name : fs::directory_iterator(category, error_code_name)) {
        if (fs::path(name).filename() == "enable" || fs::path(name).filename() == "filter") {
          continue;
        }
        TracepointInfo tracepoint_info;
        tracepoint_info.set_name(fs::path(name).filename());
        tracepoint_info.set_category(fs::path(category).filename());
        result.emplace_back(tracepoint_info);
      }
    }
  }

  error_category_message = error_code_category.message();
  error_name_message = error_code_name.message();
  if (error_category_message.compare(no_err.message()) != 0) {
    return ErrorMessage(error_category_message.c_str());
  }
  if (error_name_message.compare(no_err.message()) != 0) {
    return ErrorMessage(error_name_message.c_str());
  }

  return result;
}

std::optional<Jiffies> GetCumulativeCpuTimeFromProcess(pid_t pid) noexcept {
  const auto stat = std::filesystem::path{"/proc"} / std::to_string(pid) / "stat";

  // /proc/[pid]/stat looks like so (example - all in one line):
  // 1395261 (sleep) S 5273 1160 1160 0 -1 1077936128 101 0 0 0 0 0 0 0 20 0 1 0 42187401 5431296
  // 131 18446744073709551615 94702955896832 94702955911385 140735167078224 0 0 0 0 0 0 0 0 0 17 10
  // 0 0 0 0 0 94702955928880 94702955930112 94702967197696 140735167083224 140735167083235
  // 140735167083235 140735167086569 0
  //<commit_msg>Refactor ReadTracepoints - make it exception-free<commit_after>// Copyright (c) 2020 The Orbit Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ServiceUtils.h"

#include <absl/base/casts.h>
#include <absl/base/macros.h>
#include <absl/strings/match.h>
#include <absl/strings/numbers.h>
#include <absl/strings/str_cat.h>
#include <absl/strings/str_join.h>
#include <absl/strings/str_split.h>
#include <sys/uio.h>

#include <algorithm>
#include <filesystem>
#include <iosfwd>
#include <map>
#include <memory>
#include <numeric>
#include <outcome.hpp>
#include <set>
#include <string>
#include <string_view>
#include <system_error>
#include <utility>

#include "ElfUtils/ElfFile.h"
#include "OrbitBase/Logging.h"
#include "OrbitBase/ReadFileToString.h"
#include "OrbitBase/Result.h"
#include "absl/strings/str_format.h"
#include "module.pb.h"

namespace orbit_service::utils {

namespace fs = std::filesystem;

using ::orbit_elf_utils::ElfFile;
using orbit_grpc_protos::ModuleInfo;
using orbit_grpc_protos::TracepointInfo;

static const char* kLinuxTracingEventsDirectory = "/sys/kernel/debug/tracing/events/";

ErrorMessageOr<std::vector<orbit_grpc_protos::TracepointInfo>> ReadTracepoints() {
  std::vector<TracepointInfo> result;

  std::error_code error;
  auto category_directory_iterator = fs::directory_iterator(kLinuxTracingEventsDirectory, error);
  if (error) {
    return ErrorMessage{absl::StrFormat("Unable to scan \"%s\" directory: %s",
                                        kLinuxTracingEventsDirectory, error.message())};
  }

  for (auto category_it = fs::begin(category_directory_iterator),
            category_end = fs::end(category_directory_iterator);
       category_it != category_end; category_it.increment(error)) {
    if (error) {
      return ErrorMessage{absl::StrFormat("Unable to scan \"%s\" directory: %s",
                                          kLinuxTracingEventsDirectory, error.message())};
    }

    const fs::path& category_path = category_it->path();
    bool is_directory = fs::is_directory(category_path, error);
    if (error) {
      return ErrorMessage{
          absl::StrFormat("Unable to stat \"%s\": %s", category_path.string(), error.message())};
    }

    if (!is_directory) continue;

    auto name_directory_iterator = fs::directory_iterator(category_path, error);
    if (error) {
      return ErrorMessage{absl::StrFormat("Unable to scan \"%s\" directory: %s",
                                          category_path.string(), error.message())};
    }

    for (auto it = fs::begin(name_directory_iterator), end = fs::end(name_directory_iterator);
         it != end; it.increment(error)) {
      if (error) {
        return ErrorMessage{absl::StrFormat("Unable to scan \"%s\" directory: %s",
                                            category_path.string(), error.message())};
      }

      bool is_directory = it->is_directory(error);
      if (error) {
        return ErrorMessage{
            absl::StrFormat("Unable to stat \"%s\": %s", it->path().string(), error.message())};
      }

      if (!is_directory) {
        continue;
      }

      TracepointInfo tracepoint_info;
      tracepoint_info.set_name(it->path().filename());
      tracepoint_info.set_category(category_path.filename());
      result.emplace_back(tracepoint_info);
    }
  }

  return result;
}

std::optional<Jiffies> GetCumulativeCpuTimeFromProcess(pid_t pid) noexcept {
  const auto stat = std::filesystem::path{"/proc"} / std::to_string(pid) / "stat";

  // /proc/[pid]/stat looks like so (example - all in one line):
  // 1395261 (sleep) S 5273 1160 1160 0 -1 1077936128 101 0 0 0 0 0 0 0 20 0 1 0 42187401 5431296
  // 131 18446744073709551615 94702955896832 94702955911385 140735167078224 0 0 0 0 0 0 0 0 0 17 10
  // 0 0 0 0 0 94702955928880 94702955930112 94702967197696 140735167083224 140735167083235
  // 140735167083235 140735167086569 0
  //
{'fraction_non_alphanumeric': 0.1284496460049126, 'fraction_numerical': 0.07311082213552955, 'mean_word_length': 4.041514930808448, 'pattern_counts': {'":': 2, '<': 51, '<?xml version=': 0, '>': 55, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 9, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2107339', 'n_tokens_mistral': 2665, 'n_tokens_neox': 2323, 'n_words': 519}