cosmopolitan/third_party/python/Modules/resource.c
Justine Tunney ae5d06dc53 Unbloat build config
- 10.5% reduction of o//depend dependency graph
- 8.8% reduction in latency of make command
- Fix issue with temporary file cleanup

There's a new -w option in compile.com that turns off the recent
Landlock output path workaround for "good commands" which do not
unlink() the output file like GNU tooling does.

Our new GNU Make unveil sandboxing appears to have zero overhead
in the grand scheme of things. Full builds are pretty fast since
the only thing that's actually slowed us down is probably libcxx

    make -j16 MODE=rel
    RL: took 85,732,063µs wall time
    RL: ballooned to 323,612kb in size
    RL: needed 828,560,521µs cpu (11% kernel)
    RL: caused 39,080,670 page faults (99% memcpy)
    RL: 350,073 context switches (72% consensual)
    RL: performed 0 reads and 11,494,960 write i/o operations

pledge() and unveil() no longer consider ENOSYS to be an error.
These functions have also been added to Python's cosmo module.

This change also removes some WIN32 APIs and System Five magnums
which we're not using and it's doubtful anyone else would be too
2022-08-10 04:43:09 -07:00

396 lines
14 KiB
C

/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:4;tab-width:8;coding:utf-8 -*-│
│vi: set net ft=c ts=4 sts=4 sw=4 fenc=utf-8 :vi│
╞══════════════════════════════════════════════════════════════════════════════╡
│ Python 3 │
│ https://docs.python.org/3/license.html │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/calls/calls.h"
#include "libc/calls/struct/rlimit.h"
#include "libc/calls/struct/rusage.h"
#include "libc/calls/weirdtypes.h"
#include "libc/errno.h"
#include "libc/runtime/runtime.h"
#include "libc/sysv/consts/rlim.h"
#include "libc/sysv/consts/rlimit.h"
#include "libc/sysv/consts/rusage.h"
#include "third_party/python/Include/abstract.h"
#include "third_party/python/Include/floatobject.h"
#include "third_party/python/Include/import.h"
#include "third_party/python/Include/longobject.h"
#include "third_party/python/Include/modsupport.h"
#include "third_party/python/Include/pyerrors.h"
#include "third_party/python/Include/pymacro.h"
#include "third_party/python/Include/structseq.h"
#include "third_party/python/Include/yoink.h"
#include "third_party/python/pyconfig.h"
/* clang-format off */
PYTHON_PROVIDE("resource");
PYTHON_PROVIDE("resource.RLIMIT_AS");
PYTHON_PROVIDE("resource.RLIMIT_CORE");
PYTHON_PROVIDE("resource.RLIMIT_CPU");
PYTHON_PROVIDE("resource.RLIMIT_DATA");
PYTHON_PROVIDE("resource.RLIMIT_FSIZE");
PYTHON_PROVIDE("resource.RLIMIT_MEMLOCK");
PYTHON_PROVIDE("resource.RLIMIT_MSGQUEUE");
PYTHON_PROVIDE("resource.RLIMIT_NICE");
PYTHON_PROVIDE("resource.RLIMIT_NOFILE");
PYTHON_PROVIDE("resource.RLIMIT_NPROC");
PYTHON_PROVIDE("resource.RLIMIT_RSS");
PYTHON_PROVIDE("resource.RLIMIT_RTPRIO");
PYTHON_PROVIDE("resource.RLIMIT_RTTIME");
PYTHON_PROVIDE("resource.RLIMIT_SIGPENDING");
PYTHON_PROVIDE("resource.RLIMIT_STACK");
PYTHON_PROVIDE("resource.RLIMIT_VMEM");
PYTHON_PROVIDE("resource.RLIM_INFINITY");
PYTHON_PROVIDE("resource.RUSAGE_BOTH");
PYTHON_PROVIDE("resource.RUSAGE_CHILDREN");
PYTHON_PROVIDE("resource.RUSAGE_SELF");
PYTHON_PROVIDE("resource.RUSAGE_THREAD");
PYTHON_PROVIDE("resource.error");
PYTHON_PROVIDE("resource.getpagesize");
PYTHON_PROVIDE("resource.getrlimit");
PYTHON_PROVIDE("resource.getrusage");
PYTHON_PROVIDE("resource.setrlimit");
PYTHON_PROVIDE("resource.struct_rusage");
/* On some systems, these aren't in any header file.
On others they are, with inconsistent prototypes.
We declare the (default) return type, to shut up gcc -Wall;
but we can't declare the prototype, to avoid errors
when the header files declare it different.
Worse, on some Linuxes, getpagesize() returns a size_t... */
#define doubletime(TV) ((double)(TV).tv_sec + (TV).tv_usec * 0.000001)
PyDoc_STRVAR(struct_rusage__doc__,
"struct_rusage: Result from getrusage.\n\n"
"This object may be accessed either as a tuple of\n"
" (utime,stime,maxrss,ixrss,idrss,isrss,minflt,majflt,\n"
" nswap,inblock,oublock,msgsnd,msgrcv,nsignals,nvcsw,nivcsw)\n"
"or via the attributes ru_utime, ru_stime, ru_maxrss, and so on.");
static PyStructSequence_Field struct_rusage_fields[] = {
{"ru_utime", PyDoc_STR("user time used")},
{"ru_stime", PyDoc_STR("system time used")},
{"ru_maxrss", PyDoc_STR("max. resident set size")},
{"ru_ixrss", PyDoc_STR("shared memory size")},
{"ru_idrss", PyDoc_STR("unshared data size")},
{"ru_isrss", PyDoc_STR("unshared stack size")},
{"ru_minflt", PyDoc_STR("page faults not requiring I/O")},
{"ru_majflt", PyDoc_STR("page faults requiring I/O")},
{"ru_nswap", PyDoc_STR("number of swap outs")},
{"ru_inblock", PyDoc_STR("block input operations")},
{"ru_oublock", PyDoc_STR("block output operations")},
{"ru_msgsnd", PyDoc_STR("IPC messages sent")},
{"ru_msgrcv", PyDoc_STR("IPC messages received")},
{"ru_nsignals", PyDoc_STR("signals received")},
{"ru_nvcsw", PyDoc_STR("voluntary context switches")},
{"ru_nivcsw", PyDoc_STR("involuntary context switches")},
{0}
};
static PyStructSequence_Desc struct_rusage_desc = {
"resource.struct_rusage", /* name */
struct_rusage__doc__, /* doc */
struct_rusage_fields, /* fields */
16 /* n_in_sequence */
};
static int initialized;
static PyTypeObject StructRUsageType;
static PyObject *
resource_getrusage(PyObject *self, PyObject *args)
{
int who;
struct rusage ru;
PyObject *r;
if (!PyArg_ParseTuple(args, "i:getrusage", &who))
return NULL;
if (getrusage(who, &ru) == -1) {
if (errno == EINVAL) {
PyErr_SetString(PyExc_ValueError,
"invalid who parameter");
return NULL;
}
PyErr_SetFromErrno(PyExc_OSError);
return NULL;
}
r = PyStructSequence_New(&StructRUsageType);
if (!r)
return NULL;
PyStructSequence_SET_ITEM(r, 0, PyFloat_FromDouble(doubletime(ru.ru_utime)));
PyStructSequence_SET_ITEM(r, 1, PyFloat_FromDouble(doubletime(ru.ru_stime)));
PyStructSequence_SET_ITEM(r, 2, PyLong_FromLong(ru.ru_maxrss));
PyStructSequence_SET_ITEM(r, 3, PyLong_FromLong(ru.ru_ixrss));
PyStructSequence_SET_ITEM(r, 4, PyLong_FromLong(ru.ru_idrss));
PyStructSequence_SET_ITEM(r, 5, PyLong_FromLong(ru.ru_isrss));
PyStructSequence_SET_ITEM(r, 6, PyLong_FromLong(ru.ru_minflt));
PyStructSequence_SET_ITEM(r, 7, PyLong_FromLong(ru.ru_majflt));
PyStructSequence_SET_ITEM(r, 8, PyLong_FromLong(ru.ru_nswap));
PyStructSequence_SET_ITEM(r, 9, PyLong_FromLong(ru.ru_inblock));
PyStructSequence_SET_ITEM(r, 10, PyLong_FromLong(ru.ru_oublock));
PyStructSequence_SET_ITEM(r, 11, PyLong_FromLong(ru.ru_msgsnd));
PyStructSequence_SET_ITEM(r, 12, PyLong_FromLong(ru.ru_msgrcv));
PyStructSequence_SET_ITEM(r, 13, PyLong_FromLong(ru.ru_nsignals));
PyStructSequence_SET_ITEM(r, 14, PyLong_FromLong(ru.ru_nvcsw));
PyStructSequence_SET_ITEM(r, 15, PyLong_FromLong(ru.ru_nivcsw));
if (PyErr_Occurred()) {
Py_DECREF(r);
return NULL;
}
return r;
}
static int
py2rlimit(PyObject *limits, struct rlimit *rl_out)
{
PyObject *curobj, *maxobj;
limits = PySequence_Tuple(limits);
if (!limits)
/* Here limits is a borrowed reference */
return -1;
if (PyTuple_GET_SIZE(limits) != 2) {
PyErr_SetString(PyExc_ValueError,
"expected a tuple of 2 integers");
goto error;
}
curobj = PyTuple_GET_ITEM(limits, 0);
maxobj = PyTuple_GET_ITEM(limits, 1);
#if !defined(HAVE_LARGEFILE_SUPPORT)
rl_out->rlim_cur = PyLong_AsLong(curobj);
if (rl_out->rlim_cur == (rlim_t)-1 && PyErr_Occurred())
goto error;
rl_out->rlim_max = PyLong_AsLong(maxobj);
if (rl_out->rlim_max == (rlim_t)-1 && PyErr_Occurred())
goto error;
#else
/* The limits are probably bigger than a long */
rl_out->rlim_cur = PyLong_AsLongLong(curobj);
if (rl_out->rlim_cur == (rlim_t)-1 && PyErr_Occurred())
goto error;
rl_out->rlim_max = PyLong_AsLongLong(maxobj);
if (rl_out->rlim_max == (rlim_t)-1 && PyErr_Occurred())
goto error;
#endif
Py_DECREF(limits);
rl_out->rlim_cur = rl_out->rlim_cur & RLIM_INFINITY;
rl_out->rlim_max = rl_out->rlim_max & RLIM_INFINITY;
return 0;
error:
Py_DECREF(limits);
return -1;
}
static PyObject*
rlimit2py(struct rlimit rl)
{
if (sizeof(rl.rlim_cur) > sizeof(long)) {
return Py_BuildValue("LL",
(long long) rl.rlim_cur,
(long long) rl.rlim_max);
}
return Py_BuildValue("ll", (long) rl.rlim_cur, (long) rl.rlim_max);
}
static PyObject *
resource_getrlimit(PyObject *self, PyObject *args)
{
struct rlimit rl;
int resource;
if (!PyArg_ParseTuple(args, "i:getrlimit", &resource))
return NULL;
if (resource < 0 || resource >= RLIM_NLIMITS) {
PyErr_SetString(PyExc_ValueError,
"invalid resource specified");
return NULL;
}
if (getrlimit(resource, &rl) == -1) {
PyErr_SetFromErrno(PyExc_OSError);
return NULL;
}
return rlimit2py(rl);
}
static PyObject *
resource_setrlimit(PyObject *self, PyObject *args)
{
struct rlimit rl;
int resource;
PyObject *limits;
if (!PyArg_ParseTuple(args, "iO:setrlimit", &resource, &limits))
return NULL;
if (resource < 0 || resource >= RLIM_NLIMITS) {
PyErr_SetString(PyExc_ValueError,
"invalid resource specified");
return NULL;
}
if (py2rlimit(limits, &rl) < 0) {
return NULL;
}
if (setrlimit(resource, &rl) == -1) {
if (errno == EINVAL)
PyErr_SetString(PyExc_ValueError,
"current limit exceeds maximum limit");
else if (errno == EPERM)
PyErr_SetString(PyExc_ValueError,
"not allowed to raise maximum limit");
else
PyErr_SetFromErrno(PyExc_OSError);
return NULL;
}
Py_RETURN_NONE;
}
#ifdef HAVE_PRLIMIT
static PyObject *
resource_prlimit(PyObject *self, PyObject *args)
{
struct rlimit old_limit, new_limit;
int resource, retval;
pid_t pid;
PyObject *limits = NULL;
if (!PyArg_ParseTuple(args, _Py_PARSE_PID "i|O:prlimit",
&pid, &resource, &limits))
return NULL;
if (resource < 0 || resource >= RLIM_NLIMITS) {
PyErr_SetString(PyExc_ValueError,
"invalid resource specified");
return NULL;
}
if (limits != NULL) {
if (py2rlimit(limits, &new_limit) < 0) {
return NULL;
}
retval = prlimit(pid, resource, &new_limit, &old_limit);
}
else {
retval = prlimit(pid, resource, NULL, &old_limit);
}
if (retval == -1) {
if (errno == EINVAL) {
PyErr_SetString(PyExc_ValueError,
"current limit exceeds maximum limit");
} else {
PyErr_SetFromErrno(PyExc_OSError);
}
return NULL;
}
return rlimit2py(old_limit);
}
#endif /* HAVE_PRLIMIT */
static PyObject *
resource_getpagesize(PyObject *self, PyObject *unused)
{
long pagesize = 0;
#if defined(HAVE_GETPAGESIZE)
pagesize = getpagesize();
#elif defined(HAVE_SYSCONF)
#if defined(_SC_PAGE_SIZE)
pagesize = sysconf(_SC_PAGE_SIZE);
#else
/* Irix 5.3 has _SC_PAGESIZE, but not _SC_PAGE_SIZE */
pagesize = sysconf(_SC_PAGESIZE);
#endif
#endif
return Py_BuildValue("i", pagesize);
}
static struct PyMethodDef
resource_methods[] = {
{"getrusage", resource_getrusage, METH_VARARGS},
{"getrlimit", resource_getrlimit, METH_VARARGS},
#ifdef HAVE_PRLIMIT
{"prlimit", resource_prlimit, METH_VARARGS},
#endif
{"setrlimit", resource_setrlimit, METH_VARARGS},
{"getpagesize", resource_getpagesize, METH_NOARGS},
{NULL, NULL} /* sentinel */
};
static struct PyModuleDef resourcemodule = {
PyModuleDef_HEAD_INIT,
"resource",
NULL,
-1,
resource_methods,
NULL,
NULL,
NULL,
NULL
};
PyMODINIT_FUNC
PyInit_resource(void)
{
PyObject *m, *v;
/* Create the module and add the functions */
m = PyModule_Create(&resourcemodule);
if (m == NULL)
return NULL;
/* Add some symbolic constants to the module */
Py_INCREF(PyExc_OSError);
PyModule_AddObject(m, "error", PyExc_OSError);
if (!initialized) {
if (PyStructSequence_InitType2(&StructRUsageType,
&struct_rusage_desc) < 0)
return NULL;
}
Py_INCREF(&StructRUsageType);
PyModule_AddObject(m, "struct_rusage",
(PyObject*) &StructRUsageType);
/* insert constants */
if (RLIMIT_CPU!=127) PyModule_AddIntMacro(m, RLIMIT_CPU);
if (RLIMIT_FSIZE!=127) PyModule_AddIntMacro(m, RLIMIT_FSIZE);
if (RLIMIT_DATA!=127) PyModule_AddIntMacro(m, RLIMIT_DATA);
if (RLIMIT_STACK!=127) PyModule_AddIntMacro(m, RLIMIT_STACK);
if (RLIMIT_CORE!=127) PyModule_AddIntMacro(m, RLIMIT_CORE);
if (RLIMIT_NOFILE!=127) PyModule_AddIntMacro(m, RLIMIT_NOFILE);
if (RLIMIT_VMEM!=127) PyModule_AddIntMacro(m, RLIMIT_VMEM);
if (RLIMIT_AS!=127) PyModule_AddIntMacro(m, RLIMIT_AS);
if (RLIMIT_RSS!=127) PyModule_AddIntMacro(m, RLIMIT_RSS);
if (RLIMIT_NPROC!=127) PyModule_AddIntMacro(m, RLIMIT_NPROC);
if (RLIMIT_MEMLOCK!=127) PyModule_AddIntMacro(m, RLIMIT_MEMLOCK);
if (RLIMIT_SBSIZE!=127) PyModule_AddIntMacro(m, RLIMIT_SBSIZE);
/* Linux specific */
if (RLIMIT_MSGQUEUE!=127) PyModule_AddIntMacro(m, RLIMIT_MSGQUEUE);
if (RLIMIT_NICE!=127) PyModule_AddIntMacro(m, RLIMIT_NICE);
if (RLIMIT_RTPRIO!=127) PyModule_AddIntMacro(m, RLIMIT_RTPRIO);
if (RLIMIT_RTTIME!=127) PyModule_AddIntMacro(m, RLIMIT_RTTIME);
if (RLIMIT_SIGPENDING!=127) PyModule_AddIntMacro(m, RLIMIT_SIGPENDING);
/* FreeBSD specific */
if (RLIMIT_SWAP!=127) PyModule_AddIntMacro(m, RLIMIT_SWAP);
if (RLIMIT_SBSIZE!=127) PyModule_AddIntMacro(m, RLIMIT_SBSIZE);
if (RLIMIT_NPTS!=127) PyModule_AddIntMacro(m, RLIMIT_NPTS);
/* target */
PyModule_AddIntMacro(m, RUSAGE_SELF);
if (RUSAGE_CHILDREN!=99) PyModule_AddIntMacro(m, RUSAGE_CHILDREN);
if (RUSAGE_BOTH!=99) PyModule_AddIntMacro(m, RUSAGE_BOTH);
if (RUSAGE_THREAD!=99) PyModule_AddIntMacro(m, RUSAGE_THREAD);
if (sizeof(RLIM_INFINITY) > sizeof(long)) {
v = PyLong_FromLongLong((long long) RLIM_INFINITY);
} else
{
v = PyLong_FromLong((long) RLIM_INFINITY);
}
if (v) {
PyModule_AddObject(m, "RLIM_INFINITY", v);
}
initialized = 1;
return m;
}
_Section(".rodata.pytab.1") const struct _inittab _PyImport_Inittab_resource = {
"resource",
PyInit_resource,
};