1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
|
#include "metrics.hpp"
#include <util/folder/path.h>
#include <util/generic/maybe.h>
#include <util/stream/file.h>
namespace Porto {
TMap<TString, TMetric*> ProcMetrics;
TMetric::TMetric(const TString& name, EMetric metric) {
Name = name;
Metric = metric;
ProcMetrics[name] = this;
}
void TMetric::ClearValues(const TVector<TString>& names, TMap<TString, uint64_t>& values) const {
values.clear();
for (const auto&name : names)
values[name] = 0;
}
EError TMetric::GetValues(const TVector<TString>& names, TMap<TString, uint64_t>& values, TPortoApi& api) const {
ClearValues(names, values);
int procFd = open("/proc", O_RDONLY | O_CLOEXEC | O_DIRECTORY | O_NOCTTY);
TFileHandle procFdHandle(procFd);
if (procFd == -1)
return EError::Unknown;
TVector<TString> tids;
TidSnapshot(tids);
auto getResponse = api.Get(names, TVector<TString>{"cgroups[freezer]"});
if (getResponse == nullptr)
return EError::Unknown;
const auto containersCgroups = GetCtFreezerCgroups(getResponse);
for (const auto& tid : tids) {
const TString tidCgroup = GetFreezerCgroup(procFd, tid);
if (tidCgroup == "")
continue;
TMaybe<uint64_t> metricValue;
for (const auto& keyval : containersCgroups) {
const TString& containerCgroup = keyval.second;
if (MatchCgroups(tidCgroup, containerCgroup)) {
if (!metricValue)
metricValue = GetMetric(procFd, tid);
values[keyval.first] += *metricValue;
}
}
}
return EError::Success;
}
uint64_t TMetric::GetTidSchedMetricValue(int procFd, const TString& tid, const TString& metricName) const {
const TString schedPath = tid + "/sched";
try {
int fd = openat(procFd, schedPath.c_str(), O_RDONLY | O_CLOEXEC | O_NOCTTY, 0);
TFile file(fd);
if (!file.IsOpen())
return 0ul;
TIFStream iStream(file);
TString line;
while (iStream.ReadLine(line)) {
auto metricPos = line.find(metricName);
if (metricPos != TString::npos) {
auto valuePos = metricPos;
while (valuePos < line.size() && !::isdigit(line[valuePos]))
++valuePos;
TString value = line.substr(valuePos);
if (!value.empty() && IsNumber(value))
return IntFromString<uint64_t, 10>(value);
}
}
}
catch(...) {}
return 0ul;
}
void TMetric::GetPidTasks(const TString& pid, TVector<TString>& tids) const {
TFsPath task("/proc/" + pid + "/task");
TVector<TString> rawTids;
try {
task.ListNames(rawTids);
}
catch(...) {}
for (const auto& tid : rawTids) {
tids.push_back(tid);
}
}
void TMetric::TidSnapshot(TVector<TString>& tids) const {
TFsPath proc("/proc");
TVector<TString> rawPids;
try {
proc.ListNames(rawPids);
}
catch(...) {}
for (const auto& pid : rawPids) {
if (IsNumber(pid))
GetPidTasks(pid, tids);
}
}
TString TMetric::GetFreezerCgroup(int procFd, const TString& tid) const {
const TString cgroupPath = tid + "/cgroup";
try {
int fd = openat(procFd, cgroupPath.c_str(), O_RDONLY | O_CLOEXEC | O_NOCTTY, 0);
TFile file(fd);
if (!file.IsOpen())
return TString();
TIFStream iStream(file);
TString line;
while (iStream.ReadLine(line)) {
static const TString freezer = ":freezer:";
auto freezerPos = line.find(freezer);
if (freezerPos != TString::npos) {
line = line.substr(freezerPos + freezer.size());
return line;
}
}
}
catch(...){}
return TString();
}
TMap<TString, TString> TMetric::GetCtFreezerCgroups(const TGetResponse* response) const {
TMap<TString, TString> containersProps;
for (const auto& ctGetListResponse : response->list()) {
for (const auto& keyval : ctGetListResponse.keyval()) {
if (!keyval.error()) {
TString value = keyval.value();
static const TString freezerPath = "/sys/fs/cgroup/freezer";
if (value.find(freezerPath) != TString::npos)
value = value.substr(freezerPath.size());
containersProps[ctGetListResponse.name()] = value;
}
}
}
return containersProps;
}
bool TMetric::MatchCgroups(const TString& tidCgroup, const TString& ctCgroup) const {
if (tidCgroup.size() <= ctCgroup.size())
return tidCgroup == ctCgroup;
return ctCgroup == tidCgroup.substr(0, ctCgroup.size()) && tidCgroup[ctCgroup.size()] == '/';
}
class TCtxsw : public TMetric {
public:
TCtxsw() : TMetric(M_CTXSW, EMetric::CTXSW)
{}
uint64_t GetMetric(int procFd, const TString& tid) const override {
return GetTidSchedMetricValue(procFd, tid, "nr_switches");
}
} static Ctxsw;
} /* namespace Porto */
|