Post by Chris Iverson on Mar 14, 2019 12:53:04 GMT -5
Just something I noticed while testing SQLite on Linux.
It seems that, EVERY time LB5 makes ANY kind of call to the SQLite library, it tries (and fails) to open it again.
The reason it's failing to open it is because it's using the wrong name, "sqlite-3.so", instead of the official library name, "libsqlite3.so.0".
However, everything still works because instead of just erroring out, LB5 just uses the handle of the library from the one time it opened it successfully.
Here's a trace log that shows what's going on, taken from one complete execution of simple database.bas, with an already existing database(so no table being created).
Now, explanations of this: This is a listing of API calls made from LB5. Each line has the name of the function called, the parameters passed to it, and ends with the return value.
This is every call made to one of Linux's dynamic library functions. (dl = dynamic library). They're basically direct equivalents of the dynamic library API calls on Windows.
dlopen() is LoadLibrary(). You call this to ask the system to load a dynamic library(DLL on Windows, so on Linux) into your process. If it's successful, it returns a handle to the library. If it fails, it returns zero. In the listing above, you can see that every call made to dlopen() for "sqlite-3.so" fails(it returns zero). The one time it succeeds is when dlopen() is called for "libsqlite3.so.0", and it gets a handle value of 0x9ef77b0. (The specific value isn't important, just the fact that the same value is used in the rest of the calls to indicate it's looking for functions in the SQLite library.)
dlerror() is GetLastError(). This is called to retrieve an error code to know what specific error happened if a dynamic library API call fails. It returns 0 if there's no error(multiple calls to dlerror() that resulted in 0 were stripped from this log, for brevity). In this case, you can see it saying that it can't open the shared library "sqlite-3.so"(which makes sense, because that doesn't exist).
dlsym() is GetProcAddress(). This is what you call to be able to actually use a function in the dynamic library. You pass in the handle to the library, and the name of the function, and it returns a pointer to where that function is in memory. If an error occurs(function not found, for example), it returns 0. In this case, you can see it always being called successfully to invoke sqlite3 library functions, passing in the handle of the sqlite3 library that was opened successfully.
There's also dlclose(), which is FreeLibrary(). You call this, and pass in a library's handle, to tell the system you're not using it anymore, and to unload it from your process. Note that it's never actually called in the execution of an LB5 program(it's not listed in the trace). Once the SQLite library is loaded, it stays there until the process ends.
The thing that struck me as odd is just how it tries(and fails) to load the SQLite library literally before every single SQLite3 function call, but continues successfully due to using the stored handle. It also never tries to use the correct library name again; only once.
I don't know if this is an LB5 thing, or a VisualWorks Smalltalk thing, it just stuck out at me. I haven't done a similar trace on Windows to see if it does the same thing yet.
It seems that, EVERY time LB5 makes ANY kind of call to the SQLite library, it tries (and fails) to open it again.
The reason it's failing to open it is because it's using the wrong name, "sqlite-3.so", instead of the official library name, "libsqlite3.so.0".
However, everything still works because instead of just erroring out, LB5 just uses the handle of the library from the one time it opened it successfully.
Here's a trace log that shows what's going on, taken from one complete execution of simple database.bas, with an already existing database(so no table being created).
17150 exe->dlopen("sqlite-3.so", 257) = 0
17150 exe->dlerror() = "sqlite-3.so: cannot open shared "...
17150 exe->dlopen("libsqlite3.so.0", 257) = 0x9ef77b0
17150 exe->dlsym(0x9ef77b0, "sqlite3_open") = 0xf4f5ba30
17150 exe->dlopen("sqlite-3.so", 257) = 0
17150 exe->dlerror() = "sqlite-3.so: cannot open shared "...
17150 exe->dlsym(0x9ef77b0, "sqlite3_open") = 0xf4f5ba30
17150 exe->dlopen("sqlite-3.so", 257) = 0
17150 exe->dlerror() = "sqlite-3.so: cannot open shared "...
17150 exe->dlsym(0x9ef77b0, "sqlite3_prepare_v2") = 0xf4f2fef0
17150 exe->dlopen("sqlite-3.so", 257) = 0
17150 exe->dlerror() = "sqlite-3.so: cannot open shared "...
17150 exe->dlsym(0x9ef77b0, "sqlite3_step") = 0xf4f1f3f0
17150 exe->dlopen("sqlite-3.so", 257) = 0
17150 exe->dlerror() = "sqlite-3.so: cannot open shared "...
17150 exe->dlsym(0x9ef77b0, "sqlite3_reset") = 0xf4f152b0
17150 exe->dlopen("sqlite-3.so", 257) = 0
17150 exe->dlerror() = "sqlite-3.so: cannot open shared "...
17150 exe->dlsym(0x9ef77b0, "sqlite3_column_count") = 0xf4e993c0
17150 exe->dlopen("sqlite-3.so", 257) = 0
17150 exe->dlerror() = "sqlite-3.so: cannot open shared "...
17150 exe->dlsym(0x9ef77b0, "sqlite3_finalize") = 0xf4f109f0
17150 exe->dlopen("sqlite-3.so", 257) = 0
17150 exe->dlerror() = "sqlite-3.so: cannot open shared "...
17150 exe->dlsym(0x9ef77b0, "sqlite3_close") = 0xf4f0f3a0
17150 exe->dlopen("sqlite-3.so", 257) = 0
17150 exe->dlerror() = "sqlite-3.so: cannot open shared "...
17150 exe->dlsym(0x9ef77b0, "sqlite3_open") = 0xf4f5ba30
17150 exe->dlopen("sqlite-3.so", 257) = 0
17150 exe->dlerror() = "sqlite-3.so: cannot open shared "...
17150 exe->dlsym(0x9ef77b0, "sqlite3_open") = 0xf4f5ba30
17150 exe->dlopen("sqlite-3.so", 257) = 0
17150 exe->dlerror() = "sqlite-3.so: cannot open shared "...
17150 exe->dlsym(0x9ef77b0, "sqlite3_column_name") = 0xf4eb6040
17150 exe->dlopen("sqlite-3.so", 257) = 0
17150 exe->dlerror() = "sqlite-3.so: cannot open shared "...
17150 exe->dlsym(0x9ef77b0, "sqlite3_column_type") = 0xf4eb4ae0
17150 exe->dlopen("sqlite-3.so", 257) = 0
17150 exe->dlerror() = "sqlite-3.so: cannot open shared "...
17150 exe->dlsym(0x9ef77b0, "sqlite3_column_decltype") = 0xf4eb60a0
17150 exe->dlopen("sqlite-3.so", 257) = 0
17150 exe->dlerror() = "sqlite-3.so: cannot open shared "...
17150 exe->dlsym(0x9ef77b0, "sqlite3_column_text") = 0xf4ecbf00
17150 exe->dlopen("sqlite-3.so", 257) = 0
17150 exe->dlerror() = "sqlite-3.so: cannot open shared "...
17150 exe->dlsym(0x9ef77b0, "sqlite3_column_int64") = 0xf4eb4960
Now, explanations of this: This is a listing of API calls made from LB5. Each line has the name of the function called, the parameters passed to it, and ends with the return value.
This is every call made to one of Linux's dynamic library functions. (dl = dynamic library). They're basically direct equivalents of the dynamic library API calls on Windows.
dlopen() is LoadLibrary(). You call this to ask the system to load a dynamic library(DLL on Windows, so on Linux) into your process. If it's successful, it returns a handle to the library. If it fails, it returns zero. In the listing above, you can see that every call made to dlopen() for "sqlite-3.so" fails(it returns zero). The one time it succeeds is when dlopen() is called for "libsqlite3.so.0", and it gets a handle value of 0x9ef77b0. (The specific value isn't important, just the fact that the same value is used in the rest of the calls to indicate it's looking for functions in the SQLite library.)
dlerror() is GetLastError(). This is called to retrieve an error code to know what specific error happened if a dynamic library API call fails. It returns 0 if there's no error(multiple calls to dlerror() that resulted in 0 were stripped from this log, for brevity). In this case, you can see it saying that it can't open the shared library "sqlite-3.so"(which makes sense, because that doesn't exist).
dlsym() is GetProcAddress(). This is what you call to be able to actually use a function in the dynamic library. You pass in the handle to the library, and the name of the function, and it returns a pointer to where that function is in memory. If an error occurs(function not found, for example), it returns 0. In this case, you can see it always being called successfully to invoke sqlite3 library functions, passing in the handle of the sqlite3 library that was opened successfully.
There's also dlclose(), which is FreeLibrary(). You call this, and pass in a library's handle, to tell the system you're not using it anymore, and to unload it from your process. Note that it's never actually called in the execution of an LB5 program(it's not listed in the trace). Once the SQLite library is loaded, it stays there until the process ends.
The thing that struck me as odd is just how it tries(and fails) to load the SQLite library literally before every single SQLite3 function call, but continues successfully due to using the stored handle. It also never tries to use the correct library name again; only once.
I don't know if this is an LB5 thing, or a VisualWorks Smalltalk thing, it just stuck out at me. I haven't done a similar trace on Windows to see if it does the same thing yet.