-
Notifications
You must be signed in to change notification settings - Fork 16
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
26 changed files
with
625 additions
and
56 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
cmake_minimum_required(VERSION 3.12) | ||
|
||
project(libsbcl_librarian C) | ||
|
||
include(GNUInstallDirs) | ||
|
||
set(CMAKE_INCLUDE_CURRENT_DIR ON) | ||
|
||
add_library(sbcl_librarian SHARED libsbcl_librarian.c libsbcl_librarian.h libsbcl_librarian_err.h entry_point.c) | ||
target_link_directories(sbcl_librarian PRIVATE $ENV{BUILD_PREFIX}/lib) | ||
target_link_libraries(sbcl_librarian sbcl) | ||
|
||
add_custom_command( | ||
OUTPUT libsbcl_librarian.c libsbcl_librarian.h | ||
COMMAND ${CMAKE_COMMAND} -E env CL_SOURCE_REGISTRY=${CMAKE_CURRENT_SOURCE_DIR}/..// sbcl --script ${CMAKE_CURRENT_SOURCE_DIR}/generate-bindings.lisp | ||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} | ||
) | ||
|
||
install(TARGETS sbcl_librarian | ||
LIBRARY | ||
RUNTIME | ||
) | ||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libsbcl_librarian.core TYPE LIB) | ||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libsbcl_librarian.h TYPE INCLUDE) | ||
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/libsbcl_librarian_err.h TYPE INCLUDE) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
0.1.0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
#define LIBSBCL_API_BUILD | ||
#ifdef __linux__ | ||
#define _GNU_SOURCE | ||
#endif | ||
|
||
#ifdef _WIN32 | ||
#include <Windows.h> | ||
#include <Process.h> | ||
#include <psapi.h> | ||
#else | ||
#include <dlfcn.h> | ||
#endif | ||
|
||
#include <stdio.h> | ||
#include <stdlib.h> | ||
#include <string.h> | ||
|
||
#include "libsbcl_librarian.h" | ||
|
||
#define BUF_SIZE 1024 | ||
|
||
extern char *sbcl_runtime_home; | ||
extern char *sbcl_runtime; | ||
extern char *dir_name(char *path); | ||
extern int initialize_lisp(int argc, const char *argv[], char *envp[]); | ||
|
||
static void do_initialize_lisp(const char *libsbcl_path) | ||
{ | ||
char *libsbcl_dir = dir_name(libsbcl_path); | ||
int libsbcl_dir_len = strlen(libsbcl_dir); | ||
int core_path_size = libsbcl_dir_len + sizeof("libsbcl_librarian.core") + 1; | ||
char *core_path = malloc(core_path_size); | ||
|
||
snprintf(core_path, core_path_size, "%slibsbcl_librarian.core", libsbcl_dir); | ||
|
||
const char *init_args[] = {"", "--dynamic-space-size", "8192", "--core", core_path, "--noinform", "--no-userinit"}; | ||
|
||
initialize_lisp(sizeof(init_args) / sizeof(init_args[0]), init_args, NULL); | ||
|
||
int sbcl_home_path_size = libsbcl_dir_len + sizeof("sbcl") + 1; | ||
int libsbcl_path_size = strlen(libsbcl_path) + 1; | ||
sbcl_runtime = malloc(libsbcl_path_size); | ||
strncpy(sbcl_runtime, libsbcl_path, libsbcl_path_size); | ||
sbcl_runtime_home = malloc(sbcl_home_path_size); | ||
snprintf(sbcl_runtime_home, sbcl_home_path_size, "%ssbcl", libsbcl_dir); | ||
lisp_funcall0_by_name("os-cold-init-or-reinit", "sb-sys"); | ||
} | ||
|
||
#ifdef _WIN32 | ||
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) | ||
{ | ||
if (fdwReason == DLL_PROCESS_ATTACH) { | ||
char libsbcl_path[BUF_SIZE]; | ||
|
||
GetModuleFileNameA(hinstDLL, libsbcl_path, BUF_SIZE); | ||
do_initialize_lisp(libsbcl_path); | ||
} | ||
|
||
return TRUE; | ||
} | ||
#else | ||
__attribute__((constructor)) | ||
static void init(void) | ||
{ | ||
Dl_info info; | ||
|
||
dladdr(do_initialize_lisp, &info); | ||
do_initialize_lisp(info.dli_fname); | ||
} | ||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
(require "asdf") | ||
|
||
(asdf:load-system :sbcl-librarian) | ||
(handler-bind ((deprecation-condition #'continue)) | ||
(asdf:load-system :swank)) | ||
(in-package #:sbcl-librarian) | ||
|
||
(define-aggregate-library libsbcl-librarian (:function-linkage "LIBSBCL_LIBRARIAN_API") | ||
diagnostics | ||
environment | ||
errors | ||
handles | ||
loader) | ||
|
||
(build-bindings libsbcl-librarian "." :omit-init-function t) | ||
(build-python-bindings libsbcl-librarian "." :omit-init-call t) | ||
(build-core-and-die libsbcl-librarian ".") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
typedef enum { | ||
LISP_ERR_SUCCESS = 0, | ||
LISP_ERR_FAILURE = 1, | ||
LISP_ERR_BUG = 2, | ||
LISP_ERR_FATAL = 3 | ||
} lisp_err_t; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
[project] | ||
name = "sbcl_librarian" | ||
dynamic = ["version"] | ||
|
||
[build-system] | ||
requires = ["setuptools", "setuptools-scm"] | ||
build-backend = "setuptools.build_meta" | ||
|
||
[tool.setuptools.package-data] | ||
"*" = ["py.typed"] | ||
|
||
[tool.setuptools.dynamic] | ||
version = {attr = "sbcl_librarian.version.__version__"} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
from . import errors, raw, wrapper | ||
from .version import __version__ | ||
|
||
__all__ = ["errors", "raw", "wrapper", "__version__"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
"""Functions for debugging Lisp""" | ||
|
||
import ctypes | ||
import platform | ||
from enum import Enum | ||
|
||
import sbcl_librarian.raw | ||
|
||
|
||
def enable_backtrace() -> None: | ||
""" | ||
Enable printing backtraces when Lisp errors are | ||
signaled. | ||
""" | ||
sbcl_librarian.raw.enable_backtrace(1) | ||
|
||
|
||
def disable_backtrace() -> None: | ||
""" | ||
Disable printing backtraces when Lisp errors are | ||
signaled. | ||
""" | ||
sbcl_librarian.raw.enable_backtrace(0) | ||
|
||
|
||
def disable_debugger() -> None: | ||
"""Disable the Lisp debugger""" | ||
sbcl_librarian.raw.lisp_disable_debugger() | ||
|
||
|
||
def enable_debugger() -> None: | ||
"""Enable the lisp debugger""" | ||
sbcl_librarian.raw.lisp_enable_debugger() | ||
|
||
|
||
def gc() -> None: | ||
"""Explicitly run the Lisp garbage collector.""" | ||
sbcl_librarian.raw.lisp_gc() | ||
|
||
|
||
def memory_report() -> None: | ||
"""Print a report about memory use to standard out.""" | ||
sbcl_librarian.raw.lisp_memory_report() | ||
|
||
|
||
def lisp_memory() -> int: | ||
"""Return the memory currently used by the Lisp process""" | ||
result = ctypes.c_ulonglong() | ||
sbcl_librarian.raw.lisp_dynamic_usage(result) | ||
return result.value | ||
|
||
|
||
def start_swank_server(port: int) -> None: | ||
""" | ||
Start a swank server so that Lisp devs can interact with the Lisp | ||
process directly. | ||
""" | ||
if port <= 1024: | ||
raise ValueError("Port should be greater than 1024") | ||
|
||
sbcl_librarian.raw.lisp_start_swank_server(port) | ||
|
||
|
||
def crash() -> None: | ||
"""Signal crash in the Lisp process.""" | ||
sbcl_librarian.raw.crash() | ||
|
||
|
||
ProfilingMode = Enum("ProfilingMode", [":CPU", ":ALLOC", ":TIME"]) | ||
|
||
|
||
def start_profiling( | ||
max_samples: int = 500, | ||
mode: ProfilingMode = ProfilingMode[":CPU"], | ||
sample_interval: float = 0.01, | ||
) -> None: | ||
"""Start profiling the Lisp image. | ||
`max_samples`: The meximum number of stack traces to collect. | ||
`mode`: :CPU profiles cpu time, :TIME profiles wall clock time, | ||
:ALLOC traces thread-local allocation region overflow handlers. | ||
`sample_interval`: the number of seconds between samples. | ||
""" | ||
|
||
if platform.system() == "Windows": | ||
raise Exception("Profiling is not supported on windows") | ||
|
||
if not max_samples > 0: | ||
raise ValueError(f"max_samples must be greater than zero, not {max_samples}") | ||
|
||
if not sample_interval > 0.0: | ||
raise ValueError(f"sample_interval must be greater than zero, not {sample_interval}") | ||
|
||
args = ( | ||
f"(:max-samples {max_samples} :mode {mode.name} :sample-interval {sample_interval})".encode( | ||
"utf-8" | ||
) | ||
) | ||
sbcl_librarian.raw.lisp_start_profiling(args) | ||
|
||
|
||
def stop_profiling() -> None: | ||
"""Stop the profiler.""" | ||
if platform.system() == "Windows": | ||
raise Exception("Profiling is not supported on windows") | ||
|
||
sbcl_librarian.raw.lisp_stop_profiling() | ||
|
||
|
||
def profiler_report() -> None: | ||
"""Print a report of the results of profiling.""" | ||
if platform.system() == "Windows": | ||
raise Exception("Profiling is not supported on windows") | ||
|
||
sbcl_librarian.raw.lisp_profiler_report("(:type :FLAT)".encode("utf-8")) | ||
|
||
|
||
def profiler_reset() -> None: | ||
"""Reset the proflier.""" | ||
if platform.system() == "Windows": | ||
raise Exception("Profiling is not supported on windows") | ||
|
||
sbcl_librarian.raw.lisp_reset_profiler() | ||
|
||
|
||
def handle_count() -> int: | ||
"""Get the number of (apparently living) handles to Lisp objects.""" | ||
result = ctypes.c_int() | ||
sbcl_librarian.raw.lisp_handle_count(result) | ||
return result.value | ||
|
||
|
||
def print_error() -> None: | ||
"""Print the most recent Lisp error messages.""" | ||
result = ctypes.c_char_p() | ||
sbcl_librarian.raw.get_error_message(result) | ||
if result.value is not None: | ||
print(result.value.decode("utf-8")) # noqa: T201 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
class lisp_err_t(int): | ||
_map = { | ||
0: "LISP_ERR_SUCCESS", | ||
1: "LISP_ERR_FAILURE", | ||
2: "LISP_ERR_BUG", | ||
3: "LISP_ERR_FATAL", | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import gc | ||
from typing import Generator | ||
|
||
import pytest | ||
|
||
import sbcl_librarian.debug as debug | ||
|
||
|
||
@pytest.fixture | ||
def check_handle_leaks() -> Generator[None, None, None]: | ||
""" | ||
Fixture to check that all lisp handles are freed by the end of a test. | ||
Useful for ensuring that memory leaks within lisp do not occur. | ||
""" | ||
|
||
# Perform a GC to ensure any previously used handles are cleaned up | ||
gc.collect() | ||
|
||
# Check that there are no active handles before the test | ||
pre_count = debug.handle_count() | ||
|
||
yield | ||
|
||
# Perform a GC to ensure any handles are cleaned up | ||
gc.collect() | ||
|
||
# Check that no new handles were created | ||
post_count = debug.handle_count() | ||
assert post_count == pre_count, f"{post_count - pre_count} lisp handles were leaked." | ||
|
||
return None |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import importlib.metadata | ||
from pathlib import Path | ||
|
||
try: | ||
path = Path(__file__).parents[3] / "VERSION.txt" | ||
with Path.open(path, "r") as f: | ||
__version__ = f.readline().strip() | ||
except FileNotFoundError: | ||
__version__ = importlib.metadata.version(__name__.split(".")[0]) |
Oops, something went wrong.