Hi,
The last months I have been trying to come up with a new way to do the
debugging of the libsldap library used for native LDAP support in
Solaris. Currently most debugging is done using syslog on the LOG_DEBUG
level and this means that debugging is always on. After a discussion on
the SPARKS mailinglist we came to the conclusion that a dtrace provider
would give us a much better way to debug the library. So I started a
prototype on how this provider could look like. I started the easy way
by creating a dprintf dtrace probe which outputs a string and is a one
on one replacement of the syslog calls currently used in the code. But
from the start I wanted to be somewhat more daring is creating something
like a real dtrace provider exposing some more of the internal
structures used in the library allowing people to write dtrace code to
debug things without lots of debugging hooks in the code.
As dtrace is new to me I might not be doing the smartest moves in trying
to accomplish a dtrace provider. That''s also one of the reasons to ask
a question on this mailinglist as I''m somewhat stuck on how I could
get this to work.
The structure I''m currently am trying to export is this (I''m
using
a translator as I don''t want to export everything already (some
structures are way complex and might be added in the future but for
a prototype are now out of scope).
The top structure is the Connection structure which holds the cache
of LDAP connections. It is defined like this in the code.
typedef int ConnectionID;
/*
* This structure is used by ns_connect to create and manage
* one or more ldap connections within the library.
*/
typedef struct connection {
ConnectionID connectionId;
boolean_t usedBit; /* true if only used by
*/
/* one thread and not
shared */
/* by other threads */
boolean_t notAvail; /* not sharable, delete
*/
/* when shared == 0 */
int shared; /* number of threads */
/* using this connection
*/
pid_t pid; /* process id */
char *serverAddr;
ns_cred_t *auth;
LDAP *ld;
thread_t threadID; /* thread ID using it */
struct ns_ldap_cookie *cookieInfo;
char **controls; /* from
server_info */
char **saslMechanisms; /* from
server_info */
} Connection;
As this is an internal structure not defined in any global header files
I have added the following in my libsldap.d provider
/*
* This is a D representation of some internal structures defined in
ns_internal.h
*/
typedef int ConnectionID;
/*
* Connection structure used in most of the libsldap code. This is only
a dummy.
* entry as we specify both a 32 and 64 bits version of the structure
for alignment purposes.
*/
typedef struct connection Connection;
/*
* 32 bit representation of the structure
*/
typedef struct connection32 {
uint32_t connectionId;
uint32_t usedBit;
uint32_t notAvail;
uint32_t shared;
uint32_t pid;
uint32_t serverAddr;
uint32_t auth;
uint32_t ld;
uint32_t threadID;
uint32_t cookieInfo;
uint32_t controls;
uint32_t saslMechanisms;
} connection32_t;
/*
* 64 bit representation of the structure
*/
typedef struct connection64 {
uint64_t connectionId;
uint64_t usedBit;
uint64_t notAvail;
uint64_t shared;
uint64_t pid;
uint64_t serverAddr;
uint64_t auth;
uint64_t ld;
uint64_t threadID;
uint64_t cookieInfo;
uint64_t controls;
uint64_t saslMechanisms;
} connection64_t;
/*
* For a stable interface we translate the most important stuff into a
dtrace
* representation of the structure.
*/
typedef struct libsldap_connection {
int connectionId;
boolean_t usedBit; /* true if only
used by */
/* one thread
and not shared */
/* by other
threads */
boolean_t notAvail; /* not sharable,
delete */
/* when shared
== 0 */
int shared; /* number of
threads */
/* using this
connection */
pid_t pid; /* process id */
string serverAddr;
uintptr_t auth;
thread_t threadID; /* thread ID
using it */
} libsldap_connection_t;
And a translator to convert from the external definition to a dtrace
structure (based on the ISCSI provider which was a good startingpoint)
/*
* Translator from ns_internal.h struct Connection to dtrace defined
struct libsldap_connection
*/
#pragma D binding "1.5" translator
translator struct libsldap_connection <struct connection *P> {
connectionId = (curthread->t_procp->p_model == 0x00100000) ?
*(uint32_t *)copyin((uintptr_t)
&((connection32_t *)P)->connectionId, sizeof
(uint32_t)) :
*(uint64_t *)copyin((uintptr_t)
&((connection64_t *)P)->connectionId, sizeof
(uint64_t));
usedBit = (curthread->t_procp->p_model == 0x00100000) ?
*(uint32_t *)copyin((uintptr_t)
&((connection32_t *)P)->usedBit, sizeof (uint32_t)) :
*(uint64_t *)copyin((uintptr_t)
&((connection64_t *)P)->usedBit, sizeof (uint64_t));
notAvail = (curthread->t_procp->p_model == 0x00100000) ?
*(uint32_t *)copyin((uintptr_t)
&((connection32_t *)P)->notAvail, sizeof (uint32_t)) :
*(uint64_t *)copyin((uintptr_t)
&((connection64_t *)P)->notAvail, sizeof (uint64_t));
shared = (curthread->t_procp->p_model == 0x00100000) ?
*(uint32_t *)copyin((uintptr_t)
&((connection32_t *)P)->shared, sizeof (uint32_t)) :
*(uint64_t *)copyin((uintptr_t)
&((connection64_t *)P)->shared, sizeof (uint64_t));
pid = (curthread->t_procp->p_model == 0x00100000) ?
*(uint32_t *)copyin((uintptr_t)
&((connection32_t *)P)->pid, sizeof (uint32_t)) :
*(uint64_t *)copyin((uintptr_t)
&((connection64_t *)P)->pid, sizeof (uint64_t));
serverAddr = (curthread->t_procp->p_model == 0x00100000) ?
copyinstr((uintptr_t)*(uint32_t *)copyin((uintptr_t)
&((connection32_t *)P)->serverAddr, sizeof
(uint32_t))) :
copyinstr((uintptr_t)*(uint64_t *)copyin((uintptr_t)
&((connection64_t *)P)->serverAddr, sizeof (uint64_t)));
threadID = (curthread->t_procp->p_model == 0x00100000) ?
*(uint32_t *)copyin((uintptr_t)
&((connection32_t *)P)->threadID, sizeof (uint32_t)) :
*(uint64_t *)copyin((uintptr_t)
&((connection64_t *)P)->threadID, sizeof (uint64_t));
};
This makes the stucture available in dtrace and I can print the toplevel
element fine now.
The provider definition looks like this:
typedef struct connection {
int dummy;
} Connection;
typedef struct libsldap_connection {
int dummy;
} libsldap_connection_t;
translator libsldap_connection_t < Connection *P > {
dummy = P->dummy;
};
provider libsldap {
probe dbgmessage(char *);
probe AddConnection(Connection *connection) :
(libsldap_connection_t *connection);
probe FindConnection(Connection *connection) :
(libsldap_connection_t *connection);
probe DropConnection(Connection *connection) :
(libsldap_connection_t *connection);
};
I can now do things like this:
libsldap*:::AddConnection,
libsldap*:::FindConnection,
libsldap*:::DropConnection
{
printf("\n");
printf("%s[%d] - connectionId %d\n", execname, pid,
args[0]->connectionId);
printf("%s[%d] - usedBit %d\n", execname, pid,
args[0]->usedBit);
printf("%s[%d] - notAvail %d\n", execname, pid,
args[0]->notAvail);
printf("%s[%d] - shared %d\n", execname, pid,
args[0]->shared);
printf("%s[%d] - pid %d\n", execname, pid, args[0]->pid);
printf("%s[%d] - serveraddr %s\n", execname, pid,
args[0]->serverAddr);
printf("%s[%d] - threadid %d\n", execname, pid,
args[0]->threadID);
}
But now the fun starts I want to convert more to start with
the ???ns_cred_t structure. This is defined like this:
/*
* Authentication Information
*/
typedef enum CredLevel {
NS_LDAP_CRED_ANON = 0,
NS_LDAP_CRED_PROXY = 1,
NS_LDAP_CRED_SELF = 2
} CredLevel_t;
typedef enum AuthType {
NS_LDAP_AUTH_NONE = 0,
NS_LDAP_AUTH_SIMPLE = 1,
NS_LDAP_AUTH_SASL = 2,
NS_LDAP_AUTH_TLS = 3, /* implied SASL usage */
NS_LDAP_AUTH_ATLS = 4 /* implied SASL usage */
} AuthType_t;
typedef enum TlsType {
NS_LDAP_TLS_NONE = 0,
NS_LDAP_TLS_SIMPLE = 1,
NS_LDAP_TLS_SASL = 2
} TlsType_t;
typedef enum SaslMech {
NS_LDAP_SASL_NONE = 0, /* No SASL mechanism */
NS_LDAP_SASL_CRAM_MD5 = 1,
NS_LDAP_SASL_DIGEST_MD5 = 2,
NS_LDAP_SASL_EXTERNAL = 3, /* currently not supported */
NS_LDAP_SASL_GSSAPI = 4,
NS_LDAP_SASL_SPNEGO = 5 /* currently not supported */
} SaslMech_t;
typedef enum SaslOpt {
NS_LDAP_SASLOPT_NONE = 0,
NS_LDAP_SASLOPT_INT = 1,
NS_LDAP_SASLOPT_PRIV = 2
} SaslOpt_t;
typedef enum PrefOnly {
NS_LDAP_PREF_FALSE = 0,
NS_LDAP_PREF_TRUE = 1
} PrefOnly_t;
typedef struct UnixCred {
char *userID; /* Unix ID number */
char *passwd; /* password */
} UnixCred_t;
typedef struct CertCred {
char *path; /* certificate path */
char *passwd; /* password */
char *nickname; /* nickname */
} CertCred_t;
typedef struct ns_auth {
AuthType_t type;
TlsType_t tlstype;
SaslMech_t saslmech;
SaslOpt_t saslopt;
} ns_auth_t;
typedef struct ns_cred {
ns_auth_t auth;
char *hostcertpath;
union {
UnixCred_t unix_cred;
CertCred_t cert_cred;
} cred;
} ns_cred_t;
So I defined much of the enum in D already which
was easy. But the the fun starts. I first thought I would
write some translators for the different types and the call the
appropriate xlate functions to stuff the correct data into
the Connection structure but until now that hasn''t give me much
joy. When I try to call those translators I get the error my
expression is vaiable. So I was wondering should I just expand
the dtrace Connection structure with the different subtypes and
convert everything in that one translator, or am I missing
something obvious.
So the question is more or less, should I stick to the structure
used in the C-structs and if so how do I make that available in D,
if not should I just stuff everything in one big D structure.
Please bear with me as I''m new to dtrace, until 2 months ago
I did know it exist but never did anything with it. Currently
I''m only prototyping the stuff there is nothing final I just am trying
to see if I can make the data available so I can get ride of most
of the standard debug statements now in the code and move that into
a dtrace probe.
Marco van Wieringen