mirror of
https://github.com/matrix-construct/construct
synced 2024-11-15 14:31:11 +01:00
ircd::prof: Split prof::psi from prof_linux unit.
This commit is contained in:
parent
6ceb8c193b
commit
29b041e3a2
3 changed files with 273 additions and 262 deletions
|
@ -174,6 +174,7 @@ libircd_la_SOURCES += stats.cc
|
||||||
libircd_la_SOURCES += logger.cc
|
libircd_la_SOURCES += logger.cc
|
||||||
libircd_la_SOURCES += run.cc
|
libircd_la_SOURCES += run.cc
|
||||||
libircd_la_SOURCES += prof.cc
|
libircd_la_SOURCES += prof.cc
|
||||||
|
libircd_la_SOURCES += prof_psi.cc
|
||||||
if LINUX
|
if LINUX
|
||||||
libircd_la_SOURCES += prof_linux.cc
|
libircd_la_SOURCES += prof_linux.cc
|
||||||
endif
|
endif
|
||||||
|
|
|
@ -154,268 +154,6 @@ catch(const std::exception &e)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// prof/psi.h
|
|
||||||
//
|
|
||||||
|
|
||||||
decltype(ircd::prof::psi::supported)
|
|
||||||
ircd::prof::psi::supported
|
|
||||||
{
|
|
||||||
info::kernel_version[0] > 4 ||
|
|
||||||
(info::kernel_version[0] >= 4 && info::kernel_version[1] >= 20)
|
|
||||||
};
|
|
||||||
|
|
||||||
decltype(ircd::prof::psi::path)
|
|
||||||
ircd::prof::psi::path
|
|
||||||
{
|
|
||||||
"/proc/pressure/cpu",
|
|
||||||
"/proc/pressure/memory",
|
|
||||||
"/proc/pressure/io",
|
|
||||||
};
|
|
||||||
|
|
||||||
decltype(ircd::prof::psi::cpu)
|
|
||||||
ircd::prof::psi::cpu
|
|
||||||
{
|
|
||||||
"cpu"
|
|
||||||
};
|
|
||||||
|
|
||||||
decltype(ircd::prof::psi::mem)
|
|
||||||
ircd::prof::psi::mem
|
|
||||||
{
|
|
||||||
"memory"
|
|
||||||
};
|
|
||||||
|
|
||||||
decltype(ircd::prof::psi::io)
|
|
||||||
ircd::prof::psi::io
|
|
||||||
{
|
|
||||||
"io"
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
|
||||||
// prof::psi::metric::refresh
|
|
||||||
//
|
|
||||||
|
|
||||||
ircd::prof::psi::file &
|
|
||||||
ircd::prof::psi::wait(const vector_view<const trigger> &cmd)
|
|
||||||
try
|
|
||||||
{
|
|
||||||
static const size_t max{3};
|
|
||||||
size_t trig_num {0}, trig_idx[max]
|
|
||||||
{
|
|
||||||
size_t(-1),
|
|
||||||
size_t(-1),
|
|
||||||
size_t(-1),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Associate all of the trigger inputs (cmd) with one of the files; the
|
|
||||||
// cmds can be arranged any way and may not be for all files or any.
|
|
||||||
for(size_t i(0); i < cmd.size(); ++i)
|
|
||||||
{
|
|
||||||
const auto it
|
|
||||||
{
|
|
||||||
std::find_if(begin(path), end(path), [&cmd, &i]
|
|
||||||
(const auto &name)
|
|
||||||
{
|
|
||||||
return lstrip(name, "/proc/pressure/") == cmd[i].file.name;
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
const auto pos
|
|
||||||
{
|
|
||||||
std::distance(begin(path), it)
|
|
||||||
};
|
|
||||||
|
|
||||||
if(unlikely(size_t(pos) >= max))
|
|
||||||
throw error
|
|
||||||
{
|
|
||||||
"%s does not exist",
|
|
||||||
cmd[i].file.name,
|
|
||||||
};
|
|
||||||
|
|
||||||
trig_idx[pos] = i;
|
|
||||||
trig_num++;
|
|
||||||
}
|
|
||||||
|
|
||||||
const fs::fd::opts opts
|
|
||||||
{
|
|
||||||
std::ios::in | std::ios::out
|
|
||||||
};
|
|
||||||
|
|
||||||
// Open the fd's; if triggers were given we don't open files that were
|
|
||||||
// not included in the cmd vector; otherwise we open all files.
|
|
||||||
const fs::fd fd[max]
|
|
||||||
{
|
|
||||||
!trig_num || trig_idx[0] < max?
|
|
||||||
fs::fd{path[0], opts}:
|
|
||||||
fs::fd{},
|
|
||||||
|
|
||||||
!trig_num || trig_idx[1] < max?
|
|
||||||
fs::fd{path[1], opts}:
|
|
||||||
fs::fd{},
|
|
||||||
|
|
||||||
!trig_num || trig_idx[2] < max?
|
|
||||||
fs::fd{path[2], opts}:
|
|
||||||
fs::fd{},
|
|
||||||
};
|
|
||||||
|
|
||||||
// Write all triggers to their respective file
|
|
||||||
for(size_t i(0); i < max; ++i)
|
|
||||||
{
|
|
||||||
if(trig_idx[i] >= max)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
const auto &trig(cmd[trig_idx[i]]); try
|
|
||||||
{
|
|
||||||
// psi_write() in the kernel wants a write length of one greater
|
|
||||||
// than the length of the string, but it places a \0 in its own
|
|
||||||
// buffer unconditionally. This is noteworthy because our string
|
|
||||||
// may not be null terminated and this length requirement smells.
|
|
||||||
assert(trig.file.name == lstrip(path[i], "/proc/pressure/"));
|
|
||||||
syscall(::write, fd[i], trig.string.c_str(), size(trig.string) + 1);
|
|
||||||
}
|
|
||||||
catch(const ctx::interrupted &)
|
|
||||||
{
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
catch(const std::exception &e)
|
|
||||||
{
|
|
||||||
log::error
|
|
||||||
{
|
|
||||||
"Failed to set pressure stall trigger [%s] on /proc/pressure/%s :%s",
|
|
||||||
trig.string,
|
|
||||||
trig.file.name,
|
|
||||||
e.what(),
|
|
||||||
};
|
|
||||||
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Yield ircd::ctx until fd[n] has a result.
|
|
||||||
const size_t n
|
|
||||||
{
|
|
||||||
fs::select(fd)
|
|
||||||
};
|
|
||||||
|
|
||||||
switch(n)
|
|
||||||
{
|
|
||||||
case 0: return cpu;
|
|
||||||
case 1: return mem;
|
|
||||||
case 2: return io;
|
|
||||||
default:
|
|
||||||
always_assert(false);
|
|
||||||
__builtin_unreachable();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch(const ctx::interrupted &)
|
|
||||||
{
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
catch(const std::exception &e)
|
|
||||||
{
|
|
||||||
log::error
|
|
||||||
{
|
|
||||||
"Failed to poll pressure stall information :%s",
|
|
||||||
e.what(),
|
|
||||||
};
|
|
||||||
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
ircd::prof::psi::refresh(file &file)
|
|
||||||
noexcept try
|
|
||||||
{
|
|
||||||
if(!supported)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if(unlikely(!file.name))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
const auto &path
|
|
||||||
{
|
|
||||||
fs::path(fs::path_scratch, vector_view<const string_view>
|
|
||||||
{
|
|
||||||
"/proc/pressure"_sv, file.name
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
// Copy value into userspace
|
|
||||||
char buf[256];
|
|
||||||
fs::read_opts opts;
|
|
||||||
opts.aio = false; // can't read /proc through AIO
|
|
||||||
opts.all = false; // don't need posix read-loop; make one read(2) only.
|
|
||||||
const auto &result
|
|
||||||
{
|
|
||||||
fs::read(path, buf, opts)
|
|
||||||
};
|
|
||||||
|
|
||||||
tokens(result, '\n', [&file] // Read each line
|
|
||||||
(const string_view &line)
|
|
||||||
{
|
|
||||||
const auto &[type, vals]
|
|
||||||
{
|
|
||||||
split(line, ' ')
|
|
||||||
};
|
|
||||||
|
|
||||||
// The first token tells us what the metric is; we have allocated
|
|
||||||
// results for the following
|
|
||||||
if(type != "full" && type != "some")
|
|
||||||
return;
|
|
||||||
|
|
||||||
auto &metric
|
|
||||||
{
|
|
||||||
type == "full"?
|
|
||||||
file.full:
|
|
||||||
file.some
|
|
||||||
};
|
|
||||||
|
|
||||||
size_t i(0);
|
|
||||||
tokens(vals, ' ', [&file, &metric, &i] // Read each key=value pair
|
|
||||||
(const string_view &key_val)
|
|
||||||
{
|
|
||||||
const auto &[key, val]
|
|
||||||
{
|
|
||||||
split(key_val, '=')
|
|
||||||
};
|
|
||||||
|
|
||||||
if(key == "total")
|
|
||||||
{
|
|
||||||
const auto total(lex_cast<microseconds>(val));
|
|
||||||
metric.stall.relative = total - metric.stall.total;
|
|
||||||
metric.stall.window = duration_cast<microseconds>(now<system_point>() - file.sampled);
|
|
||||||
metric.stall.pct = metric.stall.window.count()?
|
|
||||||
metric.stall.relative.count() / double(metric.stall.window.count()):
|
|
||||||
0.0;
|
|
||||||
metric.stall.pct *= 100;
|
|
||||||
metric.stall.total = total;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if(startswith(key, "avg") && i < metric.avg.size())
|
|
||||||
{
|
|
||||||
metric.avg.at(i).window = lex_cast<seconds>(lstrip(key, "avg"));
|
|
||||||
metric.avg.at(i).pct = lex_cast<float>(val);
|
|
||||||
++i;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
file.sampled = ircd::now<system_point>();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
catch(const std::exception &e)
|
|
||||||
{
|
|
||||||
log::error
|
|
||||||
{
|
|
||||||
"Failed to refresh pressure stall information '%s' :%s",
|
|
||||||
file.name,
|
|
||||||
e.what(),
|
|
||||||
};
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// prof/instructions.h
|
// prof/instructions.h
|
||||||
|
|
272
ircd/prof_psi.cc
Normal file
272
ircd/prof_psi.cc
Normal file
|
@ -0,0 +1,272 @@
|
||||||
|
// The Construct
|
||||||
|
//
|
||||||
|
// Copyright (C) The Construct Developers, Authors & Contributors
|
||||||
|
// Copyright (C) 2016-2020 Jason Volk <jason@zemos.net>
|
||||||
|
//
|
||||||
|
// Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
// purpose with or without fee is hereby granted, provided that the above
|
||||||
|
// copyright notice and this permission notice is present in all copies. The
|
||||||
|
// full license for this software is available in the LICENSE file.
|
||||||
|
|
||||||
|
/// This unit is compiled for all targets, even though this is a linux-specific
|
||||||
|
/// interface -- for now. An explicit support condition like this could be
|
||||||
|
/// removed at some point.
|
||||||
|
decltype(ircd::prof::psi::supported)
|
||||||
|
ircd::prof::psi::supported
|
||||||
|
{
|
||||||
|
#if defined(__linux__)
|
||||||
|
info::kernel_version[0] > 4 ||
|
||||||
|
(info::kernel_version[0] >= 4 && info::kernel_version[1] >= 20)
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Position sensitive
|
||||||
|
decltype(ircd::prof::psi::path)
|
||||||
|
ircd::prof::psi::path
|
||||||
|
{
|
||||||
|
"/proc/pressure/cpu",
|
||||||
|
"/proc/pressure/memory",
|
||||||
|
"/proc/pressure/io",
|
||||||
|
};
|
||||||
|
|
||||||
|
decltype(ircd::prof::psi::cpu)
|
||||||
|
ircd::prof::psi::cpu
|
||||||
|
{
|
||||||
|
"cpu"
|
||||||
|
};
|
||||||
|
|
||||||
|
decltype(ircd::prof::psi::mem)
|
||||||
|
ircd::prof::psi::mem
|
||||||
|
{
|
||||||
|
"memory"
|
||||||
|
};
|
||||||
|
|
||||||
|
decltype(ircd::prof::psi::io)
|
||||||
|
ircd::prof::psi::io
|
||||||
|
{
|
||||||
|
"io"
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// prof::psi::metric::refresh
|
||||||
|
//
|
||||||
|
|
||||||
|
ircd::prof::psi::file &
|
||||||
|
ircd::prof::psi::wait(const vector_view<const trigger> &cmd)
|
||||||
|
try
|
||||||
|
{
|
||||||
|
static const size_t max{3};
|
||||||
|
size_t trig_num {0}, trig_idx[max]
|
||||||
|
{
|
||||||
|
size_t(-1),
|
||||||
|
size_t(-1),
|
||||||
|
size_t(-1),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Associate all of the trigger inputs (cmd) with one of the files; the
|
||||||
|
// cmds can be arranged any way and may not be for all files or any.
|
||||||
|
for(size_t i(0); i < cmd.size(); ++i)
|
||||||
|
{
|
||||||
|
const auto it
|
||||||
|
{
|
||||||
|
std::find_if(begin(path), end(path), [&cmd, &i]
|
||||||
|
(const auto &name)
|
||||||
|
{
|
||||||
|
return lstrip(name, "/proc/pressure/") == cmd[i].file.name;
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto pos
|
||||||
|
{
|
||||||
|
std::distance(begin(path), it)
|
||||||
|
};
|
||||||
|
|
||||||
|
if(unlikely(size_t(pos) >= max))
|
||||||
|
throw error
|
||||||
|
{
|
||||||
|
"%s does not exist",
|
||||||
|
cmd[i].file.name,
|
||||||
|
};
|
||||||
|
|
||||||
|
trig_idx[pos] = i;
|
||||||
|
trig_num++;
|
||||||
|
}
|
||||||
|
|
||||||
|
const fs::fd::opts opts
|
||||||
|
{
|
||||||
|
std::ios::in | std::ios::out
|
||||||
|
};
|
||||||
|
|
||||||
|
// Open the fd's; if triggers were given we don't open files that were
|
||||||
|
// not included in the cmd vector; otherwise we open all files.
|
||||||
|
const fs::fd fd[max]
|
||||||
|
{
|
||||||
|
!trig_num || trig_idx[0] < max?
|
||||||
|
fs::fd{path[0], opts}:
|
||||||
|
fs::fd{},
|
||||||
|
|
||||||
|
!trig_num || trig_idx[1] < max?
|
||||||
|
fs::fd{path[1], opts}:
|
||||||
|
fs::fd{},
|
||||||
|
|
||||||
|
!trig_num || trig_idx[2] < max?
|
||||||
|
fs::fd{path[2], opts}:
|
||||||
|
fs::fd{},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Write all triggers to their respective file
|
||||||
|
for(size_t i(0); i < max; ++i)
|
||||||
|
{
|
||||||
|
if(trig_idx[i] >= max)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const auto &trig(cmd[trig_idx[i]]); try
|
||||||
|
{
|
||||||
|
// psi_write() in the kernel wants a write length of one greater
|
||||||
|
// than the length of the string, but it places a \0 in its own
|
||||||
|
// buffer unconditionally. This is noteworthy because our string
|
||||||
|
// may not be null terminated and this length requirement smells.
|
||||||
|
assert(trig.file.name == lstrip(path[i], "/proc/pressure/"));
|
||||||
|
syscall(::write, fd[i], trig.string.c_str(), size(trig.string) + 1);
|
||||||
|
}
|
||||||
|
catch(const ctx::interrupted &)
|
||||||
|
{
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
catch(const std::exception &e)
|
||||||
|
{
|
||||||
|
log::error
|
||||||
|
{
|
||||||
|
"Failed to set pressure stall trigger [%s] on /proc/pressure/%s :%s",
|
||||||
|
trig.string,
|
||||||
|
trig.file.name,
|
||||||
|
e.what(),
|
||||||
|
};
|
||||||
|
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Yield ircd::ctx until fd[n] has a result.
|
||||||
|
const size_t n
|
||||||
|
{
|
||||||
|
fs::select(fd)
|
||||||
|
};
|
||||||
|
|
||||||
|
switch(n)
|
||||||
|
{
|
||||||
|
case 0: return cpu;
|
||||||
|
case 1: return mem;
|
||||||
|
case 2: return io;
|
||||||
|
default:
|
||||||
|
always_assert(false);
|
||||||
|
__builtin_unreachable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(const ctx::interrupted &)
|
||||||
|
{
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
catch(const std::exception &e)
|
||||||
|
{
|
||||||
|
log::error
|
||||||
|
{
|
||||||
|
"Failed to poll pressure stall information :%s",
|
||||||
|
e.what(),
|
||||||
|
};
|
||||||
|
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
ircd::prof::psi::refresh(file &file)
|
||||||
|
noexcept try
|
||||||
|
{
|
||||||
|
if(!supported)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if(unlikely(!file.name))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const auto &path
|
||||||
|
{
|
||||||
|
fs::path(fs::path_scratch, vector_view<const string_view>
|
||||||
|
{
|
||||||
|
"/proc/pressure"_sv, file.name
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
// Copy value into userspace
|
||||||
|
char buf[256];
|
||||||
|
fs::read_opts opts;
|
||||||
|
opts.aio = false; // can't read /proc through AIO
|
||||||
|
opts.all = false; // don't need posix read-loop; make one read(2) only.
|
||||||
|
const auto &result
|
||||||
|
{
|
||||||
|
fs::read(path, buf, opts)
|
||||||
|
};
|
||||||
|
|
||||||
|
tokens(result, '\n', [&file] // Read each line
|
||||||
|
(const string_view &line)
|
||||||
|
{
|
||||||
|
const auto &[type, vals]
|
||||||
|
{
|
||||||
|
split(line, ' ')
|
||||||
|
};
|
||||||
|
|
||||||
|
// The first token tells us what the metric is; we have allocated
|
||||||
|
// results for the following
|
||||||
|
if(type != "full" && type != "some")
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto &metric
|
||||||
|
{
|
||||||
|
type == "full"?
|
||||||
|
file.full:
|
||||||
|
file.some
|
||||||
|
};
|
||||||
|
|
||||||
|
size_t i(0);
|
||||||
|
tokens(vals, ' ', [&file, &metric, &i] // Read each key=value pair
|
||||||
|
(const string_view &key_val)
|
||||||
|
{
|
||||||
|
const auto &[key, val]
|
||||||
|
{
|
||||||
|
split(key_val, '=')
|
||||||
|
};
|
||||||
|
|
||||||
|
if(key == "total")
|
||||||
|
{
|
||||||
|
const auto total(lex_cast<microseconds>(val));
|
||||||
|
metric.stall.relative = total - metric.stall.total;
|
||||||
|
metric.stall.window = duration_cast<microseconds>(now<system_point>() - file.sampled);
|
||||||
|
metric.stall.pct = metric.stall.window.count()?
|
||||||
|
metric.stall.relative.count() / double(metric.stall.window.count()):
|
||||||
|
0.0;
|
||||||
|
metric.stall.pct *= 100;
|
||||||
|
metric.stall.total = total;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if(startswith(key, "avg") && i < metric.avg.size())
|
||||||
|
{
|
||||||
|
metric.avg.at(i).window = lex_cast<seconds>(lstrip(key, "avg"));
|
||||||
|
metric.avg.at(i).pct = lex_cast<float>(val);
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
file.sampled = ircd::now<system_point>();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch(const std::exception &e)
|
||||||
|
{
|
||||||
|
log::error
|
||||||
|
{
|
||||||
|
"Failed to refresh pressure stall information '%s' :%s",
|
||||||
|
file.name,
|
||||||
|
e.what(),
|
||||||
|
};
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
Loading…
Reference in a new issue