YugabyteDB (2.13.1.0-b60, 21121d69985fbf76aa6958d8f04a9bfa936293b5)

Coverage Report

Created: 2022-03-22 16:43

/Users/deen/code/yugabyte-db/src/yb/util/os-util.cc
Line
Count
Source (jump to first uncovered line)
1
// Licensed to the Apache Software Foundation (ASF) under one
2
// or more contributor license agreements.  See the NOTICE file
3
// distributed with this work for additional information
4
// regarding copyright ownership.  The ASF licenses this file
5
// to you under the Apache License, Version 2.0 (the
6
// "License"); you may not use this file except in compliance
7
// with the License.  You may obtain a copy of the License at
8
//
9
//   http://www.apache.org/licenses/LICENSE-2.0
10
//
11
// Unless required by applicable law or agreed to in writing,
12
// software distributed under the License is distributed on an
13
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
// KIND, either express or implied.  See the License for the
15
// specific language governing permissions and limitations
16
// under the License.
17
//
18
// The following only applies to changes made to this file as part of YugaByte development.
19
//
20
// Portions Copyright (c) YugaByte, Inc.
21
//
22
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
23
// in compliance with the License.  You may obtain a copy of the License at
24
//
25
// http://www.apache.org/licenses/LICENSE-2.0
26
//
27
// Unless required by applicable law or agreed to in writing, software distributed under the License
28
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
29
// or implied.  See the License for the specific language governing permissions and limitations
30
// under the License.
31
//
32
// Imported from Impala. Changes include:
33
// - Namespace and imports.
34
// - Replaced GetStrErrMsg with ErrnoToString.
35
// - Replaced StringParser with strings/numbers.
36
// - Fixes for cpplint.
37
// - Fixed parsing when thread names have spaces.
38
#include "yb/util/os-util.h"
39
40
#include <fstream>
41
#include <string>
42
#include <vector>
43
44
#include "yb/gutil/strings/split.h"
45
#include "yb/util/errno.h"
46
47
using std::ifstream;
48
using std::istreambuf_iterator;
49
using std::stringstream;
50
using strings::Split;
51
using strings::Substitute;
52
53
namespace yb {
54
55
// Ensure that Impala compiles on earlier kernels. If the target kernel does not support
56
// _SC_CLK_TCK, sysconf(_SC_CLK_TCK) will return -1.
57
#ifndef _SC_CLK_TCK
58
#define _SC_CLK_TCK 2
59
#endif
60
61
static const int64_t TICKS_PER_SEC = sysconf(_SC_CLK_TCK);
62
63
// Offsets into the ../stat file array of per-thread statistics.
64
//
65
// They are themselves offset by two because the pid and comm fields of the
66
// file are parsed separately.
67
static const int64_t USER_TICKS = 13 - 2;
68
static const int64_t KERNEL_TICKS = 14 - 2;
69
static const int64_t IO_WAIT = 41 - 2;
70
71
// Largest offset we are interested in, to check we get a well formed stat file.
72
static const int64_t MAX_OFFSET = IO_WAIT;
73
74
3
Status ParseStat(const std::string& buffer, std::string* name, ThreadStats* stats) {
75
3
  DCHECK(stats != nullptr);
76
77
  // The thread name should be the only field with parentheses. But the name
78
  // itself may contain parentheses.
79
3
  size_t open_paren = buffer.find('(');
80
3
  size_t close_paren = buffer.rfind(')');
81
3
  if (open_paren == string::npos  ||      // '(' must exist
82
3
      close_paren == string::npos ||      // ')' must exist
83
3
      open_paren >= close_paren   ||      // '(' must come before ')'
84
3
      close_paren + 2 == buffer.size()) { // there must be at least two chars after ')'
85
0
    return STATUS(IOError, "Unrecognised /proc format");
86
0
  }
87
3
  string extracted_name = buffer.substr(open_paren + 1, close_paren - (open_paren + 1));
88
3
  string rest = buffer.substr(close_paren + 2);
89
3
  vector<string> splits = Split(rest, " ", strings::SkipEmpty());
90
3
  if (splits.size() < MAX_OFFSET) {
91
0
    return STATUS(IOError, "Unrecognised /proc format");
92
0
  }
93
94
3
  int64 tmp;
95
3
  if (safe_strto64(splits[USER_TICKS], &tmp)) {
96
3
    stats->user_ns = tmp * (1e9 / TICKS_PER_SEC);
97
3
  }
98
3
  if (safe_strto64(splits[KERNEL_TICKS], &tmp)) {
99
3
    stats->kernel_ns = tmp * (1e9 / TICKS_PER_SEC);
100
3
  }
101
3
  if (safe_strto64(splits[IO_WAIT], &tmp)) {
102
3
    stats->iowait_ns = tmp * (1e9 / TICKS_PER_SEC);
103
3
  }
104
3
  if (name != nullptr) {
105
3
    *name = extracted_name;
106
3
  }
107
3
  return Status::OK();
108
109
3
}
110
111
0
Status GetThreadStats(int64_t tid, ThreadStats* stats) {
112
0
  DCHECK(stats != nullptr);
113
0
  if (TICKS_PER_SEC <= 0) {
114
0
    return STATUS(NotSupported, "ThreadStats not supported");
115
0
  }
116
117
0
  stringstream proc_path;
118
0
  proc_path << "/proc/self/task/" << tid << "/stat";
119
0
  ifstream proc_file(proc_path.str().c_str());
120
0
  if (!proc_file.is_open()) {
121
0
    return STATUS(IOError, "Could not open ifstream");
122
0
  }
123
124
0
  string buffer((istreambuf_iterator<char>(proc_file)),
125
0
      istreambuf_iterator<char>());
126
127
0
  return ParseStat(buffer, nullptr, stats); // don't want the name
128
0
}
129
130
10
bool RunShellProcess(const string& cmd, string* msg) {
131
10
  DCHECK(msg != nullptr);
132
10
  FILE* fp = popen(cmd.c_str(), "r");
133
10
  if (fp == nullptr) {
134
0
    *msg = Substitute("Failed to execute shell cmd: '$0', error was: $1", cmd,
135
0
        ErrnoToString(errno));
136
0
    return false;
137
0
  }
138
  // Read the first 1024 bytes of any output before pclose() so we have some idea of what
139
  // happened on failure.
140
10
  char buf[1024];
141
10
  size_t len = fread(buf, 1, 1024, fp);
142
10
  string output;
143
10
  output.assign(buf, len);
144
145
  // pclose() returns an encoded form of the sub-process' exit code.
146
10
  int status = pclose(fp);
147
10
  if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
148
10
    *msg = output;
149
10
    return true;
150
10
  }
151
152
0
  *msg = Substitute("Shell cmd: '$0' exited with an error: '$1'. Output was: '$2'", cmd,
153
0
      ErrnoToString(errno), output);
154
0
  return false;
155
10
}
156
157
} // namespace yb