Skip to content

Commit 67fad96

Browse files
committed
Add active_forwards to lbt_libinfo_t
This adds a new field to the `lbt_libinfo_t` structure that allows determining which BLAS/LAPACK library calls are being satisfied by which loaded libraries. By inspecting the `active_forwards` field it will be possible to, for example, walk over all loaded libraries and determine which one the `dgemm_64_` call is provided by, which may be useful for applications that, for instance, tune threading thresholds based on which BLAS provider is being used.
1 parent 1ef228e commit 67fad96

File tree

6 files changed

+108
-8
lines changed

6 files changed

+108
-8
lines changed

src/config.c

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,23 +31,56 @@ void clear_loaded_libraries() {
3131
for (int idx=0; idx<MAX_TRACKED_LIBS; ++idx) {
3232
if (lbt_config.loaded_libs[idx] != NULL) {
3333
free(lbt_config.loaded_libs[idx]->libname);
34+
free(lbt_config.loaded_libs[idx]->active_forwards);
3435
close_library(lbt_config.loaded_libs[idx]->handle);
3536
free(lbt_config.loaded_libs[idx]);
3637
lbt_config.loaded_libs[idx] = NULL;
3738
}
3839
}
3940
}
4041

41-
void record_library_load(const char * libname, void * handle, const char * suffix, int interface, int f2c) {
42+
void clear_forwarding_mark(int32_t symbol_idx, int32_t interface) {
43+
for (int idx=0; idx<MAX_TRACKED_LIBS; ++idx) {
44+
if (lbt_config.loaded_libs[idx] == NULL) {
45+
return;
46+
}
47+
if (lbt_config.loaded_libs[idx]->interface != interface) {
48+
continue;
49+
}
50+
51+
BITFIELD_CLEAR(lbt_config.loaded_libs[idx]->active_forwards, symbol_idx);
52+
}
53+
}
54+
55+
void clear_other_forwards(int skip_idx, uint8_t * forwards, int32_t interface) {
56+
for (int idx=0; idx<MAX_TRACKED_LIBS; ++idx) {
57+
if (lbt_config.loaded_libs[idx] == NULL) {
58+
return;
59+
}
60+
// Skip ourselves and things that don't match our interface
61+
if (idx == skip_idx || lbt_config.loaded_libs[idx]->interface != interface) {
62+
continue;
63+
}
64+
65+
// Flip off anything in this library that is flipped on in the passed-in forwards
66+
for (uint32_t chunk_idx=0; chunk_idx < (NUM_EXPORTED_FUNCS/8)+1; ++chunk_idx) {
67+
lbt_config.loaded_libs[idx]->active_forwards[chunk_idx] &= (forwards[chunk_idx] ^ 0xff);
68+
}
69+
}
70+
}
71+
72+
void record_library_load(const char * libname, void * handle, const char * suffix, uint8_t * forwards, int interface, int f2c) {
4273
// Scan for the an empty slot, and also check to see if this library has been loaded before.
4374
int free_idx = -1;
4475
for (int idx=0; idx<MAX_TRACKED_LIBS; ++idx) {
4576
if (lbt_config.loaded_libs[idx] == NULL) {
4677
free_idx = idx;
4778
break;
4879
}
49-
// If this library has been loaded before, just early-exit.
80+
// If this library has been loaded before, all we do is copy the `forwards` over
5081
if (lbt_config.loaded_libs[idx]->handle == handle) {
82+
memcpy(lbt_config.loaded_libs[idx]->active_forwards, forwards, sizeof(uint8_t)*(NUM_EXPORTED_FUNCS/8 + 1));
83+
clear_other_forwards(idx, forwards, interface);
5184
return;
5285
}
5386
}
@@ -63,8 +96,13 @@ void record_library_load(const char * libname, void * handle, const char * suffi
6396
memcpy(new_libinfo->libname, libname, namelen);
6497
new_libinfo->handle = handle;
6598
new_libinfo->suffix = suffix;
99+
new_libinfo->active_forwards = (uint8_t *)malloc(sizeof(uint8_t)*(NUM_EXPORTED_FUNCS/8 + 1));
100+
memcpy(new_libinfo->active_forwards, forwards, sizeof(uint8_t)*(NUM_EXPORTED_FUNCS/8 + 1));
66101
new_libinfo->interface = interface;
67102
new_libinfo->f2c = f2c;
68103

69104
lbt_config.loaded_libs[free_idx] = new_libinfo;
105+
106+
// Next, we go through and un-set all other loaded libraries of the same interface's `forwards`:
107+
clear_other_forwards(free_idx, forwards, interface);
70108
}

src/libblastrampoline.c

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,13 @@ LBT_DLLEXPORT int32_t lbt_set_forward(const char * symbol_name, const void * add
135135
if (symbol_idx == -1)
136136
return -1;
137137

138-
return set_forward_by_index(symbol_idx, addr, interface, f2c, verbose);
138+
int32_t ret = set_forward_by_index(symbol_idx, addr, interface, f2c, verbose);
139+
if (ret == 0) {
140+
// Un-mark this symbol as being provided by any of our libraries;
141+
// if you use the footgun API, you can keep track of who is providing what.
142+
clear_forwarding_mark(symbol_idx, interface);
143+
}
144+
return ret;
139145
}
140146

141147
/*
@@ -262,6 +268,7 @@ LBT_DLLEXPORT int32_t lbt_forward(const char * libname, int32_t clear, int32_t v
262268
int32_t nforwards = 0;
263269
int32_t symbol_idx = 0;
264270
char symbol_name[MAX_SYMBOL_LEN];
271+
uint8_t forwards[(NUM_EXPORTED_FUNCS/8) + 1] = {0};
265272
for (symbol_idx=0; exported_func_names[symbol_idx] != NULL; ++symbol_idx) {
266273
// If `clear` is set, zero out all symbols that may have been set so far
267274
if (clear) {
@@ -274,11 +281,12 @@ LBT_DLLEXPORT int32_t lbt_forward(const char * libname, int32_t clear, int32_t v
274281
void *addr = lookup_symbol(handle, symbol_name);
275282
if (addr != NULL) {
276283
set_forward_by_index(symbol_idx, addr, interface, f2c, verbose);
284+
BITFIELD_SET(forwards, symbol_idx);
277285
nforwards++;
278286
}
279287
}
280288

281-
record_library_load(libname, handle, lib_suffix, interface, f2c);
289+
record_library_load(libname, handle, lib_suffix, &forwards[0], interface, f2c);
282290
if (verbose) {
283291
printf("Processed %d symbols; forwarded %d symbols with %d-bit interface and mangling to a suffix of \"%s\"\n", symbol_idx, nforwards, interface, lib_suffix);
284292
}

src/libblastrampoline.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,13 @@ extern "C" {
3434
# define LBT_HIDDEN __attribute__ ((visibility("hidden")))
3535
#endif
3636

37+
#define BF_CHUNK(array, idx) (array[((uint32_t)(idx/8))])
38+
#define BF_MASK(idx) ((uint8_t)(0x1 << (idx % 8)))
39+
#define BITFIELD_GET(array, idx) ((BF_CHUNK(array, idx) & BF_MASK(idx)) >> (idx % 8))
40+
#define BITFIELD_CLEAR(array, idx) BF_CHUNK(array, idx) &= ~(BF_MASK(idx))
41+
#define BITFIELD_SET(array, idx) BF_CHUNK(array, idx) |= BF_MASK(idx)
42+
43+
3744
// The metadata stored on each loaded library
3845
typedef struct {
3946
// The library name as passed to `lbt_forward()`.
@@ -43,6 +50,11 @@ typedef struct {
4350
// The suffix used within this library as autodetected by `lbt_forward`.
4451
// Common values are `""` or `"64_"`.
4552
const char * suffix;
53+
// bitfield (in uint8_t form) representing the active forwards for this library.
54+
// Use the `BITFIELD_{SET,GET}` macros to look at particular indices within this field.
55+
// Note that if you use the footgun API (e.g. "lbt_set_forward()") these values will be
56+
// zeroed out and you must track them manually if you need to.
57+
uint8_t * active_forwards;
4658
// The interface type as autodetected by `lbt_forward`, see `LBT_INTERFACE_XXX` below
4759
int32_t interface;
4860
// The `f2c` status as autodetected by `lbt_forward`, see `LBT_F2C_XXX` below

src/libblastrampoline_internal.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,8 @@ int32_t find_symbol_idx(const char * name);
5252
// Functions in `config.c`
5353
void init_config();
5454
void clear_loaded_libraries();
55-
void record_library_load(const char * libname, void * handle, const char * suffix, int interface, int f2c);
55+
void clear_forwarding_mark(int32_t symbol_idx, int32_t interface);
56+
void record_library_load(const char * libname, void * handle, const char * suffix, uint8_t * forwards, int interface, int f2c);
5657

5758
// Functions in `win_utils.c`
5859
#ifdef _OS_WINDOWS_

test/direct.jl

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,27 @@ function unpack_loaded_libraries(config::lbt_config_t)
77
idx = 1
88
lib_ptr = unsafe_load(config.loaded_libs, idx)
99
while lib_ptr != C_NULL
10-
push!(libs, LBTLibraryInfo(unsafe_load(lib_ptr)))
10+
push!(libs, LBTLibraryInfo(unsafe_load(lib_ptr), config.num_exported_symbols))
1111

1212
idx += 1
1313
lib_ptr = unsafe_load(config.loaded_libs, idx)
1414
end
1515
return libs
1616
end
1717

18+
function find_symbol_offset(config::lbt_config_t, symbol::String)
19+
for sym_idx in 1:config.num_exported_symbols
20+
if unsafe_string(unsafe_load(config.exported_symbols, sym_idx)) == symbol
21+
return UInt32(sym_idx - 1)
22+
end
23+
end
24+
return nothing
25+
end
26+
27+
function bitfield_get(field::Vector{UInt8}, symbol_idx::UInt32)
28+
return field[div(symbol_idx,8)+1] & (UInt8(0x01) << (symbol_idx%8))
29+
end
30+
1831
lbt_prefix = get_blastrampoline_dir()
1932
lbt_handle = dlopen("$(lbt_prefix)/$(binlib)/libblastrampoline.$(shlib_ext)", RTLD_GLOBAL | RTLD_DEEPBIND)
2033

@@ -38,6 +51,10 @@ lbt_handle = dlopen("$(lbt_prefix)/$(binlib)/libblastrampoline.$(shlib_ext)", RT
3851
@test (config.build_flags & LBT_BUILDFLAGS_F2C_CAPABLE) != 0
3952
end
4053

54+
# Check to make sure that `dgemm_` is part of the exported symbols:
55+
dgemm_idx = find_symbol_offset(config, "dgemm_")
56+
@test dgemm_idx !== nothing
57+
4158
# Walk the libraries and check we have two
4259
libs = unpack_loaded_libraries(config)
4360
@test length(libs) == 2
@@ -52,13 +69,22 @@ lbt_handle = dlopen("$(lbt_prefix)/$(binlib)/libblastrampoline.$(shlib_ext)", RT
5269
@test libs[1].interface == LBT_INTERFACE_LP64
5370
end
5471
@test libs[1].f2c == LBT_F2C_PLAIN
72+
@test bitfield_get(libs[1].active_forwards, dgemm_idx) != 0
5573

5674
# Next check OpenBLAS32_jll which is always LP64
5775
@test libs[2].libname == OpenBLAS32_jll.libopenblas_path
5876
@test libs[2].suffix == ""
5977
@test libs[2].interface == LBT_INTERFACE_LP64
6078
@test libs[2].f2c == LBT_F2C_PLAIN
6179

80+
# If OpenBLAS32 and OpenBLAS are the same interface (e.g. i686)
81+
# then libs[2].active_forwards should be all zero!
82+
if libs[1].interface == libs[2].interface
83+
@test bitfield_get(libs[2].active_forwards, dgemm_idx) == 0
84+
else
85+
@test bitfield_get(libs[2].active_forwards, dgemm_idx) != 0
86+
end
87+
6288
# Load OpenBLAS32_jll again, but this time clearing it and ensure the config gets cleared too
6389
lbt_forward(lbt_handle, OpenBLAS32_jll.libopenblas_path; clear=true)
6490
config = lbt_get_config(lbt_handle)
@@ -68,7 +94,6 @@ lbt_handle = dlopen("$(lbt_prefix)/$(binlib)/libblastrampoline.$(shlib_ext)", RT
6894
@test libs[1].suffix == ""
6995
@test libs[1].interface == LBT_INTERFACE_LP64
7096
@test libs[1].f2c == LBT_F2C_PLAIN
71-
7297
end
7398

7499
@testset "get/set threads" begin
@@ -119,11 +144,25 @@ end
119144
@test slamch_32 != C_NULL
120145
@test lbt_get_forward(lbt_handle, "slamch_", LBT_INTERFACE_LP64) == slamch_32
121146

147+
# Ensure that the libs show that `slamch_` is forwarded by this library:
148+
config = lbt_get_config(lbt_handle)
149+
libs = unpack_loaded_libraries(config)
150+
@test length(libs) == 1
151+
152+
slamch_idx = find_symbol_offset(config, "slamch_")
153+
@test slamch_idx !== nothing
154+
@test bitfield_get(libs[1].active_forwards, slamch_idx) != 0
155+
orig_forwards = copy(libs[1].active_forwards)
156+
122157
# Now, test that we can muck this up
123158
my_slamch = @cfunction(record_slamch_args, Float32, (Cstring,))
124159
@test lbt_set_forward(lbt_handle, "slamch_", my_slamch, LBT_INTERFACE_LP64) == 0
125160
@test lbt_get_forward(lbt_handle, "slamch_", LBT_INTERFACE_LP64) == my_slamch
126161

162+
config = lbt_get_config(lbt_handle)
163+
libs = unpack_loaded_libraries(config)
164+
@test bitfield_get(libs[1].active_forwards, slamch_idx) == 0
165+
127166
# Ensure that we actually overrode the symbol
128167
@test ccall(dlsym(lbt_handle, "slamch_"), Float32, (Cstring,), "test") == 13.37f0
129168
@test slamch_args == ["test"]

test/utils.jl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,17 +76,19 @@ struct lbt_library_info_t
7676
libname::Cstring
7777
handle::Ptr{Cvoid}
7878
suffix::Cstring
79+
active_forwards::Ptr{UInt8}
7980
interface::Int32
8081
f2c::Int32
8182
end
8283
struct LBTLibraryInfo
8384
libname::String
8485
handle::Ptr{Cvoid}
8586
suffix::String
87+
active_forwards::Vector{UInt8}
8688
interface::Int32
8789
f2c::Int32
8890

89-
LBTLibraryInfo(x::lbt_library_info_t) = new(unsafe_string(x.libname), x.handle, unsafe_string(x.suffix), x.interface, x.f2c)
91+
LBTLibraryInfo(x::lbt_library_info_t, num_symbols::UInt32) = new(unsafe_string(x.libname), x.handle, unsafe_string(x.suffix), unsafe_wrap(Vector{UInt8}, x.active_forwards, div(num_symbols,8)+1), x.interface, x.f2c)
9092
end
9193
const LBT_INTERFACE_LP64 = 32
9294
const LBT_INTERFACE_ILP64 = 64

0 commit comments

Comments
 (0)