<commit_before>from libc.stdio cimport FILE, fopen, fclose, fgets, sprintf
from libc.stdlib cimport atoi, malloc, free, strtoul
from libc.string cimport strncmp, strlen
from posix.unistd cimport close, dup2, getpid, execve, chdir
from posix.resource cimport setrlimit, rlimit, rusage, \
    RLIMIT_AS, RLIMIT_DATA, RLIMIT_CPU, RLIMIT_STACK, RLIMIT_CORE
from posix.signal cimport kill
from posix.types cimport pid_t
from libc.signal cimport SIGSTOP

cdef extern from 'ptbox.h' nogil:
    cdef cppclass pt_debugger:
        int syscall()
        long arg0()
        long arg1()
        long arg2()
        long arg3()
        long arg4()
        long arg5()
        char *readstr(unsigned long)
        void freestr(char*)
        pid_t getpid()

    cdef cppclass pt_debugger32(pt_debugger):
        pass

    cdef cppclass pt_debugger64(pt_debugger):
        pass

    ctypedef int (*pt_handler_callback)(void *context, int syscall)
    ctypedef int (*pt_fork_handler)(void *context)
    ctypedef int (*pt_event_callback)(void *context, int event, unsigned long param)

    cdef cppclass pt_process:
        pt_process(pt_debugger *) except +
        void set_callback(pt_handler_callback callback, void* context)
        void set_event_proc(pt_event_callback, void *context)
        int set_handler(int syscall, int handler)
        int spawn(pt_fork_handler, void *context)
        int monitor()
        int getpid()
        double execution_time()
        const rusage *getrusage()

    cdef int MAX_SYSCALL

    cdef int PTBOX_EVENT_ATTACH
    cdef int PTBOX_EVENT_EXITING
    cdef int PTBOX_EVENT_EXITED
    cdef int PTBOX_EVENT_SIGNAL
    cdef int PTBOX_EVENT_PROTECTION

    cdef int PTBOX_EXIT_NORMAL
    cdef int PTBOX_EXIT_PROTECTION
    cdef int PTBOX_EXIT_SEGFAULT

cdef extern from 'dirent.h' nogil:
    ctypedef struct DIR:
        pass

    cdef struct dirent:
        char* d_name

    dirent* readdir(DIR* dirp)

cdef extern from 'sys/types.h' nogil:
    DIR *opendir(char *name)
    int closedir(DIR* dirp)

cdef extern from 'sys/ptrace.h' nogil:
    long ptrace(int, pid_t, void*, void*)
    cdef int PTRACE_TRACEME

cdef extern from 'sys/resource.h' nogil:
    cdef int RLIMIT_NPROC

cdef extern from 'signal.h' nogil:
    cdef int SIGXCPU

SYSCALL_COUNT = MAX_SYSCALL

cdef struct child_config:
    unsigned long memory # affects only sbrk heap
    unsigned long address_space # affects sbrk and mmap but not all address space is used memory
    unsigned int cpu_time # ask linus how this counts the CPU time because it SIGKILLs way before the real time limit
    int nproc
    char *file
    char *dir
    char **argv
    char **envp
    int stdin
    int stdout
    int stderr
    int max_fd
    int *fds

cdef int pt_child(void *context) nogil:
    cdef child_config *config = <child_config*> context
    cdef DIR *d = opendir('/proc/self/fd')
    cdef dirent *dir
    cdef rlimit limit
    cdef int i

    if config.address_space:
        limit.rlim_cur = limit.rlim_max = config.address_space
        setrlimit(RLIMIT_AS, &limit)

    if config.memory:
        limit.rlim_cur = limit.rlim_max = config.memory
        setrlimit(RLIMIT_DATA, &limit)

    if config.cpu_time:
        limit.rlim_cur = config.cpu_time
        limit.rlim_max = config.cpu_time + 1
        setrlimit(RLIMIT_CPU, &limit)

    if config.nproc >= 0:
        limit.rlim_cur = limit.rlim_max = config.nproc
        setrlimit(RLIMIT_NPROC, &limit)

    if config.dir[0]:
        chdir(config.dir)

    limit.rlim_cur = limit.rlim_max = 16 * 1024 * 1024
    setrlimit(RLIMIT_STACK, &limit)
    limit.rlim_cur = limit.rlim_max = 0
    setrlimit(RLIMIT_CORE, &limit)

    if config.stdin >= 0:  dup2(config.stdin, 0)
    if config.stdout >= 0: dup2(config.stdout, 1)
    if config.stderr >= 0: dup2(config.stderr, 2)

    for i in xrange(3, config.max_fd + 1):
        dup2(config.fds[i - 3], i)

    while True:
        dir = readdir(d)
        if dir == NULL:
            break
        fd = atoi(dir.d_name)
        if fd > config.max_fd:
            close(fd)
    ptrace(PTRACE_TRACEME, 0, NULL, NULL)
    kill(getpid(), SIGSTOP)
    execve(config.file, config.argv, config.envp)
    return 3306

cdef int pt_syscall_handler(void *context, int syscall) nogil:
    return (<Process>context)._syscall_handler(syscall)

cdef int pt_event_handler(void *context, int event, unsigned long param) nogil:
    return (<Process>context)._event_handler(event, param)

cdef char **alloc_string_array(list):
    cdef char **array = <char**>malloc((len(list) + 1) * sizeof(char*))
    for i, elem in enumerate(list):
        array[i] = elem
    array[len(list)] = NULL
    return array

cpdef unsigned long get_memory(pid_t pid) nogil:
    cdef unsigned long memory = 0
    cdef char path[128]
    cdef char line[128]
    cdef char *start
    cdef FILE* file
    cdef int length

    sprintf(path, '/proc/%d/status', pid)
    file = fopen(path, 'r')
    if file == NULL:
        return 0
    while True:
        if fgets(line, 128, file) == NULL:
            break
        if strncmp(line, "VmHWM:", 6) == 0:
            start = line
            length = strlen(line)
            line[length-3] = '\0'
            while not 48 <= start[0] <= 57:
                start += 1
            memory = strtoul(start, NULL, 0)
            break
    fclose(file)
    return memory


cdef class Debugger:
    cdef pt_debugger *thisptr

    def syscall(self):
        return self.thisptr.syscall()

    property arg0:
        def __get__(self):
            return self.thisptr.arg0()

    property arg1:
        def __get__(self):
            return self.thisptr.arg1()

    property arg2:
        def __get__(self):
            return self.thisptr.arg2()

    property arg3:
        def __get__(self):
            return self.thisptr.arg3()

    property arg4:
        def __get__(self):
            return self.thisptr.arg4()

    property arg5:
        def __get__(self):
            return self.thisptr.arg5()

    property uarg0:
        def __get__(self):
            return <unsigned long>self.thisptr.arg0()

    property uarg1:
        def __get__(self):
            return <unsigned long>self.thisptr.arg1()

    property uarg2:
        def __get__(self):
            return <unsigned long>self.thisptr.arg2()

    property uarg3:
        def __get__(self):
            return <unsigned long>self.thisptr.arg3()

    property uarg4:
        def __get__(self):
            return <unsigned long>self.thisptr.arg4()

    property uarg5:
        def __get__(self):
            return <unsigned long>self.thisptr.arg5()

    def readstr(self, unsigned long address):
        cdef char* str = self.thisptr.readstr(address)
        pystr = <object>str
        self.thisptr.freestr(str)
        return pystr

    def getpid(self):
        return self.thisptr.getpid()


cdef class Process:
    cdef pt_debugger *_debugger
    cdef pt_process *process
    cdef public Debugger debugger
    cdef readonly bint _exited
    cdef readonly int _exitcode
    cdef public int _child_stdin, _child_stdout, _child_stderr
    cdef public unsigned long _child_memory, _child_address<commit_msg>Exposed poking to python code.<commit_after>from libc.stdio cimport FILE, fopen, fclose, fgets, sprintf
from libc.stdlib cimport atoi, malloc, free, strtoul
from libc.string cimport strncmp, strlen
from posix.unistd cimport close, dup2, getpid, execve, chdir
from posix.resource cimport setrlimit, rlimit, rusage, \
    RLIMIT_AS, RLIMIT_DATA, RLIMIT_CPU, RLIMIT_STACK, RLIMIT_CORE
from posix.signal cimport kill
from posix.types cimport pid_t
from libc.signal cimport SIGSTOP

cdef extern from 'ptbox.h' nogil:
    cdef cppclass pt_debugger:
        int syscall()
        long arg0()
        long arg1()
        long arg2()
        long arg3()
        long arg4()
        long arg5()
        char *readstr(unsigned long)
        void freestr(char*)
        pid_t getpid()

    cdef cppclass pt_debugger32(pt_debugger):
        pass

    cdef cppclass pt_debugger64(pt_debugger):
        pass

    ctypedef int (*pt_handler_callback)(void *context, int syscall)
    ctypedef int (*pt_fork_handler)(void *context)
    ctypedef int (*pt_event_callback)(void *context, int event, unsigned long param)

    cdef cppclass pt_process:
        pt_process(pt_debugger *) except +
        void set_callback(pt_handler_callback callback, void* context)
        void set_event_proc(pt_event_callback, void *context)
        int set_handler(int syscall, int handler)
        int spawn(pt_fork_handler, void *context)
        int monitor()
        int getpid()
        double execution_time()
        const rusage *getrusage()

    cdef int MAX_SYSCALL

    cdef int PTBOX_EVENT_ATTACH
    cdef int PTBOX_EVENT_EXITING
    cdef int PTBOX_EVENT_EXITED
    cdef int PTBOX_EVENT_SIGNAL
    cdef int PTBOX_EVENT_PROTECTION

    cdef int PTBOX_EXIT_NORMAL
    cdef int PTBOX_EXIT_PROTECTION
    cdef int PTBOX_EXIT_SEGFAULT

cdef extern from 'dirent.h' nogil:
    ctypedef struct DIR:
        pass

    cdef struct dirent:
        char* d_name

    dirent* readdir(DIR* dirp)

cdef extern from 'sys/types.h' nogil:
    DIR *opendir(char *name)
    int closedir(DIR* dirp)

cdef extern from 'sys/ptrace.h' nogil:
    long ptrace(int, pid_t, void*, void*)
    cdef int PTRACE_TRACEME

cdef extern from 'sys/resource.h' nogil:
    cdef int RLIMIT_NPROC

cdef extern from 'signal.h' nogil:
    cdef int SIGXCPU

SYSCALL_COUNT = MAX_SYSCALL

cdef struct child_config:
    unsigned long memory # affects only sbrk heap
    unsigned long address_space # affects sbrk and mmap but not all address space is used memory
    unsigned int cpu_time # ask linus how this counts the CPU time because it SIGKILLs way before the real time limit
    int nproc
    char *file
    char *dir
    char **argv
    char **envp
    int stdin
    int stdout
    int stderr
    int max_fd
    int *fds

cdef int pt_child(void *context) nogil:
    cdef child_config *config = <child_config*> context
    cdef DIR *d = opendir('/proc/self/fd')
    cdef dirent *dir
    cdef rlimit limit
    cdef int i

    if config.address_space:
        limit.rlim_cur = limit.rlim_max = config.address_space
        setrlimit(RLIMIT_AS, &limit)

    if config.memory:
        limit.rlim_cur = limit.rlim_max = config.memory
        setrlimit(RLIMIT_DATA, &limit)

    if config.cpu_time:
        limit.rlim_cur = config.cpu_time
        limit.rlim_max = config.cpu_time + 1
        setrlimit(RLIMIT_CPU, &limit)

    if config.nproc >= 0:
        limit.rlim_cur = limit.rlim_max = config.nproc
        setrlimit(RLIMIT_NPROC, &limit)

    if config.dir[0]:
        chdir(config.dir)

    limit.rlim_cur = limit.rlim_max = 16 * 1024 * 1024
    setrlimit(RLIMIT_STACK, &limit)
    limit.rlim_cur = limit.rlim_max = 0
    setrlimit(RLIMIT_CORE, &limit)

    if config.stdin >= 0:  dup2(config.stdin, 0)
    if config.stdout >= 0: dup2(config.stdout, 1)
    if config.stderr >= 0: dup2(config.stderr, 2)

    for i in xrange(3, config.max_fd + 1):
        dup2(config.fds[i - 3], i)

    while True:
        dir = readdir(d)
        if dir == NULL:
            break
        fd = atoi(dir.d_name)
        if fd > config.max_fd:
            close(fd)
    ptrace(PTRACE_TRACEME, 0, NULL, NULL)
    kill(getpid(), SIGSTOP)
    execve(config.file, config.argv, config.envp)
    return 3306

cdef int pt_syscall_handler(void *context, int syscall) nogil:
    return (<Process>context)._syscall_handler(syscall)

cdef int pt_event_handler(void *context, int event, unsigned long param) nogil:
    return (<Process>context)._event_handler(event, param)

cdef char **alloc_string_array(list):
    cdef char **array = <char**>malloc((len(list) + 1) * sizeof(char*))
    for i, elem in enumerate(list):
        array[i] = elem
    array[len(list)] = NULL
    return array

cpdef unsigned long get_memory(pid_t pid) nogil:
    cdef unsigned long memory = 0
    cdef char path[128]
    cdef char line[128]
    cdef char *start
    cdef FILE* file
    cdef int length

    sprintf(path, '/proc/%d/status', pid)
    file = fopen(path, 'r')
    if file == NULL:
        return 0
    while True:
        if fgets(line, 128, file) == NULL:
            break
        if strncmp(line, "VmHWM:", 6) == 0:
            start = line
            length = strlen(line)
            line[length-3] = '\0'
            while not 48 <= start[0] <= 57:
                start += 1
            memory = strtoul(start, NULL, 0)
            break
    fclose(file)
    return memory


cdef class Debugger:
    cdef pt_debugger *thisptr

    def syscall(self):
        return self.thisptr.syscall()

    property arg0:
        def __get__(self):
            return self.thisptr.arg0()

        def __set__(self, value):
            self.thisptr.arg0(<long>value)

    property arg1:
        def __get__(self):
            return self.thisptr.arg1()

        def __set__(self, value):
            self.thisptr.arg1(<long>value)

    property arg2:
        def __get__(self):
            return self.thisptr.arg2()

        def __set__(self, value):
            self.thisptr.arg2(<long>value)

    property arg3:
        def __get__(self):
            return self.thisptr.arg3()

        def __set__(self, value):
            self.thisptr.arg3(<long>value)

    property arg4:
        def __get__(self):
            return self.thisptr.arg4()

        def __set__(self, value):
            self.thisptr.arg4(<long>value)

    property arg5:
        def __get__(self):
            return self.thisptr.arg5()

        def __set__(self, value):
            self.thisptr.arg5(<long>value)

    property uarg0:
        def __get__(self):
            return <unsigned long>self.thisptr.arg0()

        def __set__(self, value):
            self.thisptr.arg0(<long><unsigned long>value)

    property uarg1:
        def __get__(self):
            return <unsigned long>self.thisptr.arg1()

        def __set__(self, value):
            self.thisptr.arg1(<long><unsigned long>value)

    property uarg2:
        def __get__(self):
            return <unsigned long>self.thisptr.arg2()

        def __set__(self, value):
            self.thisptr.arg2(<long><unsigned long>value)

    property uarg3:
        def __get__(self):
            return <unsigned long>self.thisptr.arg3()

        def __set__(self, value):
            self.thisptr.arg3(<long><unsigned long>value)

    property uarg4:
        def __get__(self):
            return <unsigned long>self.thisptr.arg4()

        def __set__(self, value):
            self.thisptr.arg4(<long><unsigned long>value)

    property uarg5:
        def __get__(self):
            return <unsigned long>self.thisptr.arg5()

        def __set__(self, value):
            self.thisptr.arg5(<long><unsigned long>value)

    def readstr(self, unsigned long address):
        cdef char* str = self.thisptr.readstr(address)
        pystr = <object>str
        self.thisptr.freestr(str)
        return pystr

    def getpid(self):
        return self.thisptr.getpid()


cdef class Process:
    cdef pt_debugger *_debugger
    cdef pt_process *process
    cdef public Debugger debugger
    cdef readonly bint _exited
    cdef readonly int _exitcode
    cdef public int _child_stdin, _child_stdout, _child_stderr
    cdef public unsigned long _child_memory, _child_address
{'fraction_non_alphanumeric': 0.08863562357538261, 'fraction_numerical': 0.012504070335395637, 'mean_word_length': 2.507537688442211, 'pattern_counts': {'":': 0, '<': 47, '<?xml version=': 0, '>': 53, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2835851', 'n_tokens_mistral': 5251, 'n_tokens_neox': 5034, 'n_words': 1377}