PolarDBforPostgreSQL/external/polar_monitor/polar_monitor_cgroup.c

656 lines
17 KiB
C

/*-------------------------------------------------------------------------
*
* polar_monitor_cgroup.c
* views of polardb control group
*
* Copyright (c) 2021, Alibaba Group Holding Limited
*
* 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.
*
* IDENTIFICATION
* external/polar_monitor/polar_monitor_cgroup.c
*-------------------------------------------------------------------------
*/
#include "polar_monitor.h"
#define MAX_LINE 1024
const int64 S_2_NS = 1000000000;
const int64 UNDEFINED_VALUE = -2;
const int64 NOT_RESTRICTION_VALUE = -1;
char rel_path[MAX_LINE];
char base_path[MAX_LINE];
/*
* Declarations
*/
static void add_various_tuple(Tuplestorestate *state, TupleDesc tdesc, char* sqltype, char* cmdtype,
int64 count, Datum *values, bool *nulls, int array_size);
static int64 get_cpu_usage(void);
static int64 get_cpu_user(void);
static int64 get_cpu_system(void);
static int64 get_mem_usage(void);
static int64 get_huge_in_bytes(void);
static int64 get_io_serviced(void);
static int64 get_io_service_bytes(void);
static void add_mem_tuples(Tuplestorestate *state, TupleDesc tdesc,
Datum *values, bool *nulls, int array_size);
static int64 get_value_from_file(char* file);
static int64 get_value_from_last_line(char* file);
static int64 get_value_from_key(char* file, char* key);
static int64 get_value_from_blkio(char* file);
static int64 get_value_from_cpus(char* file);
static bool is_number(char c);
static int get_next_num(char* data, int* pos, int len);
static int64 get_memory_limit(void);
static int64 get_read_bps(void);
static int64 get_read_iops(void);
static int64 get_write_bps(void);
static int64 get_write_iops(void);
static int64 get_cfs_period(void);
static int64 get_cfs_quota(void);
static int64 get_rt_period(void);
static int64 get_rt_runtime(void);
static int64 get_cpus(void);
static char* get_relative_path(char* subsystem);
static char* get_base_path(void);
static void set_base_path(char* basepath);
static int64 get_ratio_user_hz_2_ns(void);
/*
* Add various query type to tuple.
*/
static void
add_various_tuple(Tuplestorestate *state, TupleDesc tdesc, char* subtype, char* infotype,
int64 count, Datum *values, bool *nulls, int array_size)
{
int col = 0;
if (count == UNDEFINED_VALUE)
{
elog(WARNING, "Get invalid value from %s.%s, check whether the file exist", subtype, infotype);
return;
}
MemSet(values, 0, sizeof(Datum) * array_size);
MemSet(nulls, 0, sizeof(bool) * array_size);
values[col++] = CStringGetTextDatum(subtype);
values[col++] = CStringGetTextDatum(infotype);
values[col++] = Int64GetDatumFast(count);
tuplestore_putvalues(state, tdesc, values, nulls);
}
/*
* Use this function when we want to obtain content with pattern: [%ld] in file
*/
static int64
get_value_from_file(char* file)
{
FILE *fp;
char buf[MAX_LINE];
int64 value = UNDEFINED_VALUE;
fp = fopen(file, "r");
if (fp == NULL)
return value;
if (fgets(buf, MAX_LINE, fp) != NULL)
sscanf(buf, "%ld", &value);
fclose(fp);
return value;
}
/*
* Use this function when we want to obtain content with pattern: [%s %ld] in last line
*/
static int64
get_value_from_last_line(char* file)
{
FILE *fp;
char buf[MAX_LINE];
char str[MAX_LINE];
int64 value = UNDEFINED_VALUE;
fp = fopen(file, "r");
if (fp == NULL)
return value;
while (fgets(buf, MAX_LINE, fp) != NULL);
sscanf(buf, "%s %ld", str, &value);
fclose(fp);
return value;
}
/*
* Use this function when we want to obtain content with pattern: [%s %ld] in line
*/
static int64
get_value_from_key(char* file, char* key)
{
FILE *fp;
char buf[MAX_LINE];
char str[MAX_LINE];
int64 value = UNDEFINED_VALUE;
fp = fopen(file, "r");
if (fp == NULL)
return value;
while (fgets(buf, MAX_LINE, fp) != NULL)
{
sscanf(buf, "%s %ld", str, &value);
if(strcmp(str, key) == 0)
break;
}
fclose(fp);
return value;
}
/*
* Use this function when we want to obtain content with pattern: [%ld:%ld %ld] in line
*/
static int64
get_value_from_blkio(char* file)
{
FILE *fp;
char buf[MAX_LINE];
int64 master = UNDEFINED_VALUE;
int64 slave = UNDEFINED_VALUE;
int64 quato = UNDEFINED_VALUE;
fp = fopen(file, "r");
if (fp == NULL)
return quato;
/* if the file is empty, there is no restriction */
quato = NOT_RESTRICTION_VALUE;
if (fgets(buf, MAX_LINE, fp) != NULL)
{
sscanf(buf, "%ld:%ld %ld", &master, &slave, &quato);
}
fclose(fp);
return quato;
}
/*
* A helper function to judge whether a char is bwtween '0' and '9'
*/
static bool
is_number(char c)
{
return c >= '0' && c <= '9';
}
/*
* A helper function to parse the next number in string.
* After paring, pos move to next position.
*/
static int
get_next_num(char* data, int* pos, int len)
{
int p = *pos;
int sum = data[p] - '0';
int cur_num;
while (p + 1 < len && is_number(data[p + 1]))
{
++p;
cur_num = data[p] - '0';
sum = sum * 10 + cur_num;
}
++p;
*pos = p;
return sum;
}
/*
* A simple parser to calculate the number that represent in file with pattern: [digit digit-digit],*[digit digit-digit]
* eg: For "0-3,5,7-10", we should return 9.
* Time Complexity: O(n)
*/
static int64
get_value_from_cpus(char* file)
{
FILE *fp;
char buf[MAX_LINE];
int64 count = 0;
fp = fopen(file, "r");
if (fp == NULL)
return count;
if (fgets(buf, MAX_LINE, fp) != NULL)
{
int len = strlen(buf);
int pos = 0;
int eNum;
while (pos < len)
{
int bNum = get_next_num(buf, &pos, len);
if (pos >= len)
{
++count;
break;
}
if (buf[pos] == ',')
{
++count;
++pos;
}else if (buf[pos] == '-')
{
++pos;
eNum = get_next_num(buf, &pos, len);
count += eNum - bNum + 1;
++pos;
}
}
}
fclose(fp);
return count;
}
static int64
get_cpu_usage()
{
char path[MAX_LINE];
sprintf(path, "%s%scpu/cpuacct.usage", get_base_path(), get_relative_path("cpu"));
return get_value_from_file(path);
}
static int64
get_ratio_user_hz_2_ns()
{
int64 ticks = sysconf( _SC_CLK_TCK );
return S_2_NS / ticks;
}
static int64
get_cpu_user()
{
char path[MAX_LINE];
int64 user_value;
sprintf(path, "%s%scpu/cpuacct.stat", get_base_path(), get_relative_path("cpu"));
user_value = get_value_from_key(path, "user");
user_value *= get_ratio_user_hz_2_ns();
return user_value;
}
static int64
get_cpu_system()
{
char path[MAX_LINE];
int64 sys_value;
sprintf(path, "%s%scpu/cpuacct.stat", get_base_path(), get_relative_path("cpu"));
sys_value = get_value_from_key(path, "system");
sys_value *= get_ratio_user_hz_2_ns();
return sys_value;
}
static int64
get_mem_usage()
{
char path[MAX_LINE];
sprintf(path, "%s%smemory/memory.usage_in_bytes", get_base_path(), get_relative_path("memory"));
return get_value_from_file(path);
}
static int64
get_huge_in_bytes()
{
char path[MAX_LINE];
sprintf(path, "%s%shugetlb/hugetlb.2MB.usage_in_bytes", get_base_path(), get_relative_path("hugetlb"));
return get_value_from_file(path);
}
static int64
get_io_serviced()
{
char path[MAX_LINE];
sprintf(path, "%s%sblkio/blkio.throttle.io_serviced", get_base_path(), get_relative_path("blkio"));
return get_value_from_last_line(path);
}
static int64
get_io_service_bytes()
{
char path[MAX_LINE];
sprintf(path, "%s%sblkio/blkio.throttle.io_service_bytes", get_base_path(), get_relative_path("blkio"));
return get_value_from_last_line(path);
}
static int64
get_memory_limit()
{
char path[MAX_LINE];
sprintf(path, "%s%smemory/memory.limit_in_bytes", get_base_path(), get_relative_path("memory"));
return get_value_from_file(path);
}
static int64
get_read_bps()
{
char path[MAX_LINE];
sprintf(path, "%s%sblkio/blkio.throttle.read_bps_device", get_base_path(), get_relative_path("blkio"));
return get_value_from_blkio(path);
}
static int64
get_read_iops()
{
char path[MAX_LINE];
sprintf(path, "%s%sblkio/blkio.throttle.read_iops_device", get_base_path(), get_relative_path("blkio"));
return get_value_from_blkio(path);
}
static int64
get_write_bps()
{
char path[MAX_LINE];
sprintf(path, "%s%sblkio/blkio.throttle.write_bps_device", get_base_path(), get_relative_path("blkio"));
return get_value_from_blkio(path);
}
static int64
get_write_iops()
{
char path[MAX_LINE];
sprintf(path, "%s%sblkio/blkio.throttle.write_iops_device", get_base_path(), get_relative_path("blkio"));
return get_value_from_blkio(path);
}
static int64
get_cfs_period()
{
char path[MAX_LINE];
sprintf(path, "%s%scpu/cpu.cfs_period_us", get_base_path(), get_relative_path("cpu"));
return get_value_from_file(path);
}
static int64
get_cfs_quota()
{
char path[MAX_LINE];
sprintf(path, "%s%scpu/cpu.cfs_quota_us", get_base_path(), get_relative_path("cpu"));
return get_value_from_file(path);
}
static int64
get_rt_period()
{
char path[MAX_LINE];
sprintf(path, "%s%scpu/cpu.rt_period_us", get_base_path(), get_relative_path("cpu"));
return get_value_from_file(path);
}
static int64
get_rt_runtime()
{
char path[MAX_LINE];
sprintf(path, "%s%scpu/cpu.rt_runtime_us", get_base_path(), get_relative_path("cpu"));
return get_value_from_file(path);
}
static int64
get_cpus()
{
char path[MAX_LINE];
sprintf(path, "%s%scpu/cpuset.cpus", get_base_path(), get_relative_path("cpu"));
return get_value_from_cpus(path);
}
static char*
get_base_path()
{
return base_path;
}
static void
set_base_path(char* basepath)
{
sprintf(base_path, "%s", basepath);
}
/*
* Get relative path from /proc/self/cgroup.
* Return default value '/' if not found the subsystem in file.
* Time Complexity: O(n)
*/
static char*
get_relative_path(char* subsystem)
{
char buf[MAX_LINE];
int num;
int p_start;
int len;
char subsystems[MAX_LINE];
FILE *fp = fopen("/proc/self/cgroup", "r");
if (fp == NULL)
return "/";
while (fgets(buf, MAX_LINE, fp) != NULL)
{
bool is_found = false;
int i = 0;
sscanf(buf, "%d:%[^:]:%s", &num, subsystems, rel_path);
len = strlen(subsystems);
subsystems[len] = ',';
subsystems[len + 1] = '\0';
p_start = 0;
for (; i <= len; i++)
{
if (subsystems[i] == ',')
{
subsystems[i] = '\0';
if (strcmp(subsystem, subsystems + p_start) == 0)
{
is_found = true;
break;
}
p_start = i + 1;
}
}
if (is_found)
{
len = strlen(rel_path);
if (len > 1)
{
rel_path[len] = '/';
rel_path[len + 1] = '\0';
}
fclose(fp);
return rel_path;
}
}
fclose(fp);
return "/";
}
/*
* Add all memory usage infos in file memroty.stat that with prefix 'total_'.
*/
static void
add_mem_tuples(Tuplestorestate *state, TupleDesc tdesc, Datum *values,
bool *nulls, int array_size)
{
FILE *fp;
char buf[MAX_LINE];
char str[MAX_LINE];
int64 value;
char tmp;
char* pattern = "total_";
int end_pos = strlen(pattern);
char path[MAX_LINE];
sprintf(path, "%s%smemory/memory.stat", get_base_path(), get_relative_path("memory"));
fp = fopen(path, "r");
if (fp == NULL)
{
elog(WARNING, "Failed to open memory.stat");
return;
}
while (fgets(buf, MAX_LINE, fp) != NULL)
{
sscanf(buf, "%s %ld", str, &value);
tmp = str[end_pos];
str[end_pos] = '\0';
if (strcmp(str, pattern) == 0)
{
str[end_pos] = tmp;
add_various_tuple(state, tdesc, "memory", str,
value, values, nulls, array_size);
}
}
fclose(fp);
}
PG_FUNCTION_INFO_V1(polar_stat_cgroup);
/*
* Collect the cgroup info for creating the view.
*/
Datum
polar_stat_cgroup(PG_FUNCTION_ARGS)
{
#define POLAR_STAT_CGROUP_COLS 3
int index = 1;
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
TupleDesc tupdesc;
Tuplestorestate *tupstore;
MemoryContext per_query_ctx;
MemoryContext oldcontext;
Datum values[POLAR_STAT_CGROUP_COLS];
bool nulls[POLAR_STAT_CGROUP_COLS];
/* check to see if caller supports us returning a tuplestore */
if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("set-valued function called in context that cannot accept a set")));
if (!(rsinfo->allowedModes & SFRM_Materialize))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("materialize mode required, but it is not " \
"allowed in this context")));
tupdesc = CreateTemplateTupleDesc(POLAR_STAT_CGROUP_COLS, false);
TupleDescInitEntry(tupdesc, (AttrNumber) index++, "sub_type",
TEXTOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) index++, "info_type",
TEXTOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) index++, "count",
INT8OID, -1, 0);
per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
oldcontext = MemoryContextSwitchTo(per_query_ctx);
tupstore = tuplestore_begin_heap(true, false, work_mem);
rsinfo->returnMode = SFRM_Materialize;
rsinfo->setResult = tupstore;
rsinfo->setDesc = tupdesc;
MemoryContextSwitchTo(oldcontext);
set_base_path("/sys/fs/cgroup");
/* cpu */
add_various_tuple(tupstore, tupdesc, "cpu", "cpuacct.usage",
get_cpu_usage(), values, nulls, POLAR_STAT_CGROUP_COLS);
add_various_tuple(tupstore, tupdesc, "cpu", "cpuacct.stat.user",
get_cpu_user(), values, nulls, POLAR_STAT_CGROUP_COLS);
add_various_tuple(tupstore, tupdesc, "cpu", "cpuacct.stat.system",
get_cpu_system(), values, nulls, POLAR_STAT_CGROUP_COLS);
/* memory */
add_various_tuple(tupstore, tupdesc, "memory", "memory.usage_in_bytes",
get_mem_usage(), values, nulls, POLAR_STAT_CGROUP_COLS);
add_mem_tuples(tupstore, tupdesc, values, nulls, POLAR_STAT_CGROUP_COLS);
/* huge page */
add_various_tuple(tupstore, tupdesc, "hugepage", "hugetlb.2MB.usage_in_bytes",
get_huge_in_bytes(), values, nulls, POLAR_STAT_CGROUP_COLS);
/* io */
add_various_tuple(tupstore, tupdesc, "io", "blkio.throttle.io_serviced",
get_io_serviced(), values, nulls, POLAR_STAT_CGROUP_COLS);
add_various_tuple(tupstore, tupdesc, "io", "blkio.throttle.io_service_bytes",
get_io_service_bytes(), values, nulls, POLAR_STAT_CGROUP_COLS);
/* clean up and return the tuplestore */
tuplestore_donestoring(tupstore);
return (Datum) 0;
}
PG_FUNCTION_INFO_V1(polar_cgroup_quota);
/*
* Collect the cgroup quota info for creating the view.
*/
Datum
polar_cgroup_quota(PG_FUNCTION_ARGS)
{
#define POLAR_CGROUP_QUOTA_COLS 3
int index = 1;
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
TupleDesc tupdesc;
Tuplestorestate *tupstore;
MemoryContext per_query_ctx;
MemoryContext oldcontext;
Datum values[POLAR_CGROUP_QUOTA_COLS];
bool nulls[POLAR_CGROUP_QUOTA_COLS];
/* check to see if caller supports us returning a tuplestore */
if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("set-valued function called in context that cannot accept a set")));
if (!(rsinfo->allowedModes & SFRM_Materialize))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("materialize mode required, but it is not " \
"allowed in this context")));
tupdesc = CreateTemplateTupleDesc(POLAR_CGROUP_QUOTA_COLS, false);
TupleDescInitEntry(tupdesc, (AttrNumber) index++, "sub_type",
TEXTOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) index++, "info_type",
TEXTOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) index++, "count",
INT8OID, -1, 0);
per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
oldcontext = MemoryContextSwitchTo(per_query_ctx);
tupstore = tuplestore_begin_heap(true, false, work_mem);
rsinfo->returnMode = SFRM_Materialize;
rsinfo->setResult = tupstore;
rsinfo->setDesc = tupdesc;
MemoryContextSwitchTo(oldcontext);
set_base_path("/sys/fs/cgroup");
/* cpu */
add_various_tuple(tupstore, tupdesc, "cpu", "cpu.cfs_period_us",
get_cfs_period(), values, nulls, POLAR_CGROUP_QUOTA_COLS);
add_various_tuple(tupstore, tupdesc, "cpu", "cpu.cfs_quota_us",
get_cfs_quota(), values, nulls, POLAR_CGROUP_QUOTA_COLS);
add_various_tuple(tupstore, tupdesc, "cpu", "cpu.rt_period_us",
get_rt_period(), values, nulls, POLAR_CGROUP_QUOTA_COLS);
add_various_tuple(tupstore, tupdesc, "cpu", "cpu.rt_runtime_us",
get_rt_runtime(), values, nulls, POLAR_CGROUP_QUOTA_COLS);
add_various_tuple(tupstore, tupdesc, "cpu", "cpuset.cpus",
get_cpus(), values, nulls, POLAR_CGROUP_QUOTA_COLS);
/* memory */
add_various_tuple(tupstore, tupdesc, "memory", "memory.limit_in_bytes",
get_memory_limit(), values, nulls, POLAR_CGROUP_QUOTA_COLS);
/* io */
add_various_tuple(tupstore, tupdesc, "blkio", "throttle.read_bps_device",
get_read_bps(), values, nulls, POLAR_CGROUP_QUOTA_COLS);
add_various_tuple(tupstore, tupdesc, "blkio", "throttle.read_iops_device",
get_read_iops(), values, nulls, POLAR_CGROUP_QUOTA_COLS);
add_various_tuple(tupstore, tupdesc, "blkio", "throttle.write_bps_device",
get_write_bps(), values, nulls, POLAR_CGROUP_QUOTA_COLS);
add_various_tuple(tupstore, tupdesc, "blkio", "throttle.write_iops_device",
get_write_iops(), values, nulls, POLAR_CGROUP_QUOTA_COLS);
/* clean up and return the tuplestore */
tuplestore_donestoring(tupstore);
return (Datum) 0;
}