folly/folly/io/async/test/TimeUtil.cpp

/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * 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.
 */

#ifndef __STDC_FORMAT_MACROS
#define __STDC_FORMAT_MACROS
#endif

#include <folly/io/async/test/TimeUtil.h>

#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>

#include <cerrno>
#ifdef __linux__
#include <sys/utsname.h>
#endif

#include <chrono>
#include <ostream>
#include <stdexcept>

#include <glog/logging.h>

#include <folly/Conv.h>
#include <folly/Portability.h>
#include <folly/ScopeGuard.h>
#include <folly/String.h>
#include <folly/portability/Unistd.h>
#include <folly/system/ThreadId.h>

string;
usingnamespacestd::chrono;

namespace folly {

#ifdef __linux__
static int getLinuxVersion(StringPiece release) {
  auto dot1 = release.find('.');
  if (dot1 == StringPiece::npos) {
    throw std::invalid_argument("could not find first dot");
  }
  auto v1 = folly::to<int>(release.subpiece(0, dot1));

  auto dot2 = release.find('.', dot1 + 1);
  if (dot2 == StringPiece::npos) {
    throw std::invalid_argument("could not find second dot");
  }
  auto v2 = folly::to<int>(release.subpiece(dot1 + 1, dot2 - (dot1 + 1)));

  int dash = release.find('-', dot2 + 1);
  auto v3 = folly::to<int>(release.subpiece(dot2 + 1, dash - (dot2 + 1)));

  return ((v1 * 1000 + v2) * 1000) + v3;
}

/**
 * Determine the time units used in /proc/<pid>/schedstat
 *
 * Returns the number of nanoseconds per time unit,
 * or -1 if we cannot determine the units.
 */
static int64_t determineSchedstatUnits() {
  struct utsname unameInfo;
  if (uname(&unameInfo) != 0) {
    LOG(ERROR) << "unable to determine jiffies/second: uname failed: %s"
               << errnoStr(errno);
    return -1;
  }

  // In Linux version 2.6.23 and later, time time values are always
  // reported in nanoseconds.
  //
  // This change appears to have been made in commit 425e0968a25f, which
  // moved some of the sched stats code to a new file.  Despite the commit
  // message claiming "no code changes are caused by this patch", it changed
  // the task.sched_info.cpu_time and task.sched_info.run_delay counters to be
  // computed using sched_clock() rather than jiffies.
  int linuxVersion;
  try {
    linuxVersion = getLinuxVersion(unameInfo.release);
  } catch (const std::exception&) {
    LOG(ERROR) << "unable to determine jiffies/second: failed to parse "
               << "kernel release string \"" << unameInfo.release << "\"";
    return -1;
  }
  if (linuxVersion >= 2006023) {
    // The units are nanoseconds
    return 1;
  }

  // In Linux versions prior to 2.6.23, the time values are reported in
  // jiffies.  This is somewhat unfortunate, as the number of jiffies per
  // second is configurable.  We have to determine the units being used.
  //
  // It seems like the only real way to figure out the CONFIG_HZ value used by
  // this kernel is to look it up in the config file.
  //
  // Look in /boot/config-<kernel_release>
  char configPath[256];
  snprintf(
      configPath, sizeof(configPath), "/boot/config-%s", unameInfo.release);

  FILE* f = fopen(configPath, "r");
  if (f == nullptr) {
    LOG(ERROR) << "unable to determine jiffies/second: "
                  "cannot open kernel config file %s"
               << configPath;
    return -1;
  }
  SCOPE_EXIT {
    fclose(f);
  };

  int64_t hz = -1;
  char buf[1024];
  while (fgets(buf, sizeof(buf), f) != nullptr) {
    if (strcmp(buf, "CONFIG_NO_HZ=y\n") == 0) {
      LOG(ERROR) << "unable to determine jiffies/second: tickless kernel";
      return -1;
    } else if (strcmp(buf, "CONFIG_HZ=1000\n") == 0) {
      hz = 1000;
    } else if (strcmp(buf, "CONFIG_HZ=300\n") == 0) {
      hz = 300;
    } else if (strcmp(buf, "CONFIG_HZ=250\n") == 0) {
      hz = 250;
    } else if (strcmp(buf, "CONFIG_HZ=100\n") == 0) {
      hz = 100;
    }
  }

  if (hz == -1) {
    LOG(ERROR) << "unable to determine jiffies/second: no CONFIG_HZ setting "
                  "found in %s"
               << configPath;
    return -1;
  }

  return hz;
}
#endif

/**
 * Determine how long this process has spent waiting to get scheduled on the
 * CPU.
 *
 * Returns the number of nanoseconds spent waiting, or -1 if the amount of
 * time cannot be determined.
 */
static nanoseconds getSchedTimeWaiting(pid_t tid) {}

void TimePoint::reset() {}

std::ostream& operator<<(std::ostream& os, const TimePoint& timePoint) {}

bool checkTimeout(
    const TimePoint& start,
    const TimePoint& end,
    nanoseconds expected,
    bool allowSmaller,
    nanoseconds tolerance) {}

} // namespace folly