Skip to content

Commit

Permalink
WUP
Browse files Browse the repository at this point in the history
  • Loading branch information
pablogsal committed Oct 1, 2024
1 parent 60ff67d commit 04e3419
Show file tree
Hide file tree
Showing 12 changed files with 671 additions and 0 deletions.
2 changes: 2 additions & 0 deletions Include/cpython/pystate.h
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,8 @@ struct _ts {
The PyThreadObject must hold the only reference to this value.
*/
PyObject *threading_local_sentinel;

int debugger_pending_call;
};

#ifdef Py_DEBUG
Expand Down
5 changes: 5 additions & 0 deletions Include/internal/pycore_runtime.h
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,11 @@ typedef struct _Py_DebugOffsets {
uint64_t size;
uint64_t collecting;
} gc;

struct _debugger_support {
uint64_t eval_breaker;
uint64_t debugger_pending_call;
} debugger_support;
} _Py_DebugOffsets;

/* Reference tracer state */
Expand Down
4 changes: 4 additions & 0 deletions Include/internal/pycore_runtime_init.h
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,10 @@ extern PyTypeObject _PyExc_MemoryError;
.size = sizeof(struct _gc_runtime_state), \
.collecting = offsetof(struct _gc_runtime_state, collecting), \
}, \
.debugger_support = { \
.eval_breaker = offsetof(PyThreadState, eval_breaker), \
.debugger_pending_call = offsetof(PyThreadState, debugger_pending_call), \
}, \
}, \
.allocators = { \
.standard = _pymem_allocators_standard_INIT(runtime), \
Expand Down
77 changes: 77 additions & 0 deletions Modules/_testexternalinspection.c
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,59 @@ read_memory(pid_t pid, void* remote_address, size_t len, void* dst)
return total_bytes_read;
}

ssize_t
write_memory(pid_t pid, void* remote_address, size_t len, const void* src)
{
ssize_t total_bytes_written = 0;
#if defined(__linux__) && HAVE_PROCESS_VM_WRITEV
struct iovec local[1];
struct iovec remote[1];
ssize_t result = 0;
ssize_t written = 0;

do {
local[0].iov_base = (void*)((char*)src + result);
local[0].iov_len = len - result;
remote[0].iov_base = (void*)((char*)remote_address + result);
remote[0].iov_len = len - result;

written = process_vm_writev(pid, local, 1, remote, 1, 0);
if (written < 0) {
PyErr_SetFromErrno(PyExc_OSError);
return -1;
}

result += written;
} while ((size_t)written != local[0].iov_len);
total_bytes_written = result;
#elif defined(__APPLE__) && TARGET_OS_OSX
ssize_t result = -1;
kern_return_t kr = mach_vm_write(
pid_to_task(pid),
(mach_vm_address_t)remote_address,
(vm_offset_t)src,
(mach_msg_type_number_t)len);

if (kr != KERN_SUCCESS) {
switch (kr) {
case KERN_PROTECTION_FAILURE:
PyErr_SetString(PyExc_PermissionError, "Not enough permissions to write memory");
break;
case KERN_INVALID_ARGUMENT:
PyErr_SetString(PyExc_PermissionError, "Invalid argument to mach_vm_write");
break;
default:
PyErr_SetString(PyExc_RuntimeError, "Unknown error writing memory");
}
return -1;
}
total_bytes_written = len;
#else
return -1;
#endif
return total_bytes_written;
}

int
read_string(pid_t pid, _Py_DebugOffsets* debug_offsets, void* address, char* buffer, Py_ssize_t size)
{
Expand Down Expand Up @@ -588,6 +641,30 @@ get_stack_trace(PyObject* self, PyObject* args)

// No Python frames are available for us (can happen at tear-down).
if (address_of_thread != NULL) {

uintptr_t eval_breaker;
(void)read_memory(
pid,
(void*)(address_of_thread + local_debug_offsets.debugger_support.eval_breaker),
sizeof(uintptr_t),
&eval_breaker);

eval_breaker |= (1U <<5);

(void)write_memory(
pid,
(void*)(address_of_thread + local_debug_offsets.debugger_support.eval_breaker),
sizeof(uintptr_t),
&eval_breaker);

int pending_call = 1;
(void)write_memory(
pid,
(void*)(address_of_thread + local_debug_offsets.debugger_support.debugger_pending_call),
sizeof(int),
&pending_call);


void* address_of_current_frame;
(void)read_memory(
pid,
Expand Down
14 changes: 14 additions & 0 deletions Python/ceval_gil.c
Original file line number Diff line number Diff line change
Expand Up @@ -1315,5 +1315,19 @@ _Py_HandlePending(PyThreadState *tstate)
return -1;
}
}

if (tstate->debugger_pending_call) {
tstate->debugger_pending_call = 0;
PyObject* debug = _PyImport_GetModuleAttrString("debug", "debug_with_fifo");
if (debug == NULL) {
return -1;
}
printf("Debug: %p\n", debug);
PyObject* result = PyObject_CallNoArgs(debug);
if (!result) {
return -1;
}
Py_DECREF(result);
}
return 0;
}
2 changes: 2 additions & 0 deletions Python/pystate.c
Original file line number Diff line number Diff line change
Expand Up @@ -1738,6 +1738,8 @@ PyThreadState_Clear(PyThreadState *tstate)

Py_CLEAR(tstate->context);

tstate->debugger_pending_call = 0;

#ifdef Py_GIL_DISABLED
// Each thread should clear own freelists in free-threading builds.
struct _Py_freelists *freelists = _Py_freelists_GET();
Expand Down
185 changes: 185 additions & 0 deletions asserts.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
code = """
# Python Assert Statement Examples
# Basic assertions
assert True
assert not False
assert 1 == 1
assert 2 + 2 == 4
assert len("hello") == 5
assert "python" in "I love python programming"
assert isinstance(42, int)
assert callable(len)
# Numeric assertions
assert 10 > 5
assert 7 < 9
assert 3 <= 3
assert 4 >= 4
assert 2**3 == 8
assert 10 % 3 == 1
assert abs(-5) == 5
assert round(3.14159, 2) == 3.14
# String assertions
assert "hello".upper() == "HELLO"
assert "WORLD".lower() == "world"
assert "python".capitalize() == "Python"
assert " strip ".strip() == "strip"
assert "hello world".split() == ["hello", "world"]
assert ",".join(["a", "b", "c"]) == "a,b,c"
assert "python".startswith("py")
assert "python".endswith("on")
# List assertions
assert [1, 2, 3] + [4, 5] == [1, 2, 3, 4, 5]
assert [1, 2, 3] * 2 == [1, 2, 3, 1, 2, 3]
assert 2 in [1, 2, 3]
assert 4 not in [1, 2, 3]
assert [1, 2, 3].index(2) == 1
assert [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5].count(5) == 3
assert sorted([3, 1, 4, 1, 5, 9, 2]) == [1, 1, 2, 3, 4, 5, 9]
assert list(reversed([1, 2, 3])) == [3, 2, 1]
# Dictionary assertions
assert {"a": 1, "b": 2} == {"b": 2, "a": 1}
assert "key" in {"key": "value"}
assert "value" in {"key": "value"}.values()
assert len({"a": 1, "b": 2, "c": 3}) == 3
assert {"a": 1, "b": 2}.get("c", 3) == 3
assert list({"a": 1, "b": 2}.keys()) == ["a", "b"]
assert list({"a": 1, "b": 2}.values()) == [1, 2]
assert list({"a": 1, "b": 2}.items()) == [("a", 1), ("b", 2)]
# Set assertions
assert {1, 2, 3} == {3, 2, 1}
assert {1, 2, 3}.issubset({1, 2, 3, 4, 5})
assert {1, 2, 3, 4, 5}.issuperset({1, 2, 3})
assert {1, 2, 3}.union({3, 4, 5}) == {1, 2, 3, 4, 5}
assert {1, 2, 3}.intersection({2, 3, 4}) == {2, 3}
assert {1, 2, 3}.difference({2, 3, 4}) == {1}
assert {1, 2, 3}.symmetric_difference({2, 3, 4}) == {1, 4}
# Type assertions
assert type(42) is int
assert type("hello") is str
assert type([1, 2, 3]) is list
assert type({"a": 1}) is dict
assert type({1, 2, 3}) is set
assert type((1, 2, 3)) is tuple
# Identity assertions
a = [1, 2, 3]
b = a
c = [1, 2, 3]
assert a is b
assert a is not c
assert a == c
# Membership assertions
assert 2 in [1, 2, 3]
assert 4 not in [1, 2, 3]
assert "h" in "hello"
assert "x" not in "hello"
# Boolean logic assertions
assert True and True
assert not (True and False)
assert True or False
assert not (False or False)
assert True is not False
assert (True or False) and (True and not False)
# Comparison assertions
assert 1 < 2 < 3
assert 3 > 2 > 1
assert 1 <= 1 <= 2
assert 2 >= 2 >= 1
assert 1 != 2 != 3
# Math function assertions
import math
assert math.sqrt(16) == 4
assert math.ceil(3.14) == 4
assert math.floor(3.14) == 3
assert math.isclose(0.1 + 0.2, 0.3, rel_tol=1e-9)
assert math.gcd(18, 24) == 6
assert math.lcm(4, 6) == 12
# String method assertions
assert "hello".replace("l", "L") == "heLLo"
assert "hello world".title() == "Hello World"
assert " hello ".strip() == "hello"
assert "hello".zfill(10) == "00000hello"
assert "hello".center(11, "-") == "---hello---"
assert "hello".isalpha()
assert "123".isdigit()
assert "hello123".isalnum()
assert "HELLO".isupper()
assert "hello".islower()
# List comprehension assertion
assert [x**2 for x in range(5)] == [0, 1, 4, 9, 16]
# Generator expression assertion
assert list(x**2 for x in range(5)) == [0, 1, 4, 9, 16]
# Zip function assertion
assert list(zip([1, 2, 3], ["a", "b", "c"])) == [(1, "a"), (2, "b"), (3, "c")]
# Enumerate function assertion
assert list(enumerate(["a", "b", "c"])) == [(0, "a"), (1, "b"), (2, "c")]
# Any and all function assertions
assert any([False, True, False])
assert all([True, True, True])
assert not any([False, False, False])
assert not all([True, False, True])
# Map function assertion
assert list(map(str, [1, 2, 3])) == ["1", "2", "3"]
# Filter function assertion
assert list(filter(lambda x: x % 2 == 0, [1, 2, 3, 4, 5])) == [2, 4]
# Sum function assertion
assert sum([1, 2, 3, 4, 5]) == 15
# Max and min function assertions
assert max([1, 5, 3, 2, 4]) == 5
assert min([1, 5, 3, 2, 4]) == 1
# Sorted function with key assertion
assert sorted(["apple", "banana", "cherry"], key=len) == ["apple", "cherry", "banana"]
# Lambda function assertion
assert (lambda x: x**2)(4) == 16
# Class and instance assertions
class TestClass:
def __init__(self, value):
self.value = value
def double(self):
return self.value * 2
test_instance = TestClass(5)
assert isinstance(test_instance, TestClass)
assert hasattr(test_instance, "value")
assert hasattr(test_instance, "double")
assert test_instance.value == 5
assert test_instance.double() == 10
# Exception assertions
try:
1 / 0
except ZeroDivisionError:
assert True
else:
assert False
"""
compile(code, "a", "exec")
Loading

0 comments on commit 04e3419

Please sign in to comment.