User-land Statically Defined Tracing in C++
DTrace is a comprehensive dynamic tracing framework for the Solaris operating system. DTrace provides a powerful infrastructure to
answer arbitrary questions about the behavior of the operating system
and user applications. One could develop applications which exploit this feature. The paradigms to get this done are different. To introduce DTrace capabilities in your application, the DTrace framework offers you the interface through Userland Statically Define Tracing, shortly known as USDT. With USDT, the state of your application could be clearly ascertained and with that information, the necessary course of actions undertaken. In this article, I shall show you how to introduce DTrace probes in a C++ application and the associated issues.
Many people suppose that C/C++ is dead with the advent of Java. However, C/C++ applications are still continued to be developed and deployed. Several applications and even programming languages or databases such as MySQL, PhP, Java, etc. are still developed and deployed in C/C++. With DTrace, the possibilities of tracing applications and gaining an overview in a "non-destructive" way, has opened the opportunities for its usage on production quality systems. From a developer perspective, you could incrementally introduce DTrace probes in the critical paths of your code. Typically, 20% of a software's lifetime lies in the developmental phase and 80% of it is in maintainence.
To ease the debugging of applications, be it on a development system or on a production machine, DTrace offers you wide possibilities. However, the problem is not that simple. When considering C/C++, the way DTrace handles them differently. This is because of the name-mangling phenomenon, that flattens the namespaces of C++ applications. Why should this be done?
UNIX is a flat world too. UNIX understands flat namespaces and was developed or is still developed with the C-language and as a result has a flat namespace. C++ offers the hierarchial namespaces, whereby its possible to organize information in a hierarchial way. UNIX does not understand this hierarchy. Thats a problem! C++ overcomes this by flattening the namespace that makes UNIX happy. This phenomenon is called the name-decoration or name-mangling or namespace mangling and so on.
Let me illustrate this with a small program, written in C and compiled with the Sun Studio compiler ( C & C++ compilers). Lets us call this program hallo.c which has nothing spectacular, but has a simple function, void printHallo(int), taking an integer argument. This function prints the word, "Hallo", n-times depending upon the argument you pass to this function. The program listing is as follows:
meson:/export/home/mithun/USDT >more hallo.c
#include <stdio.h>
void printHallo(int);
void printHallo(int n){
int i = 0;
for(int i =0; i < n; i++){
printf("Hallo\n"
;
}
}
int main(void){
printHallo(5);
}
Compile and run this program with C compiler (observe the cc in lower case, which means a C-Compiler) as follows:
meson:/export/home/mithun/USDT >cc hallo.c -o hallo
meson:/export/home/mithun/USDT >./hallo
Hallo
Hallo
Hallo
Hallo
Hallo
meson:/export/home/mithun/USDT >
Let us use the same code and compile it with C++ compiler
meson:/export/home/mithun/USDT >CC hallo.c -o hallo
meson:/export/home/mithun/USDT >./hallo
Hallo
Hallo
Hallo
Hallo
Hallo
The program behaves the same way. However, the internal workings are different. To demonstrate how the program is linked, we shall use the "nm" facility from Solaris and dissect the binary, hallo.
hallo:
[Index] Value Size Type Bind Other Shndx Name
[30] | 0| 0|SECT |LOCL |0 |29 |
[2] | 134545652| 0|SECT |LOCL |0 |1 |
[3] | 134545672| 0|SECT |LOCL |0 |2 |
[4] | 134545688| 0|SECT |LOCL |0 |3 |
[5] | 134545920| 0|SECT |LOCL |0 |4 |
[6] | 134546064| 0|SECT |LOCL |0 |5 |
[7] | 134546496| 0|SECT |LOCL |0 |6 |
[8] | 134547388| 0|SECT |LOCL |0 |7 |
[9] | 134547436| 0|SECT |LOCL |0 |8 |
[10] | 134547496| 0|SECT |LOCL |0 |9 |
[11] | 134547504| 0|SECT |LOCL |0 |10 |
[12] | 134547552| 0|SECT |LOCL |0 |11 |
[13] | 134547664| 0|SECT |LOCL |0 |12 |
[14] | 134548036| 0|SECT |LOCL |0 |13 |
[15] | 134548064| 0|SECT |LOCL |0 |14 |
[16] | 134548092| 0|SECT |LOCL |0 |15 |
[17] | 134548096| 0|SECT |LOCL |0 |16 |
[18] | 134613640| 0|SECT |LOCL |0 |17 |
[19] | 134613676| 0|SECT |LOCL |0 |18 |
[20] | 134613996| 0|SECT |LOCL |0 |19 |
[21] | 134614072| 0|SECT |LOCL |0 |20 |
[22] | 134614072| 0|SECT |LOCL |0 |21 |
[23] | 0| 0|SECT |LOCL |0 |22 |
[24] | 0| 0|SECT |LOCL |0 |23 |
[25] | 0| 0|SECT |LOCL |0 |24 |
[26] | 0| 0|SECT |LOCL |0 |25 |
[27] | 0| 0|SECT |LOCL |0 |26 |
[28] | 0| 0|SECT |LOCL |0 |27 |
[29] | 0| 0|SECT |LOCL |0 |28 |
[45] | 134614072| 0|OBJT |LOCL |0 |21 |Bbss.bss
[42] | 134614072| 0|NOTY |LOCL |0 |21 |Bbss.bss
[46] | 134613996| 0|OBJT |LOCL |0 |19 |Ddata.data
[41] | 134613996| 0|NOTY |LOCL |0 |19 |Ddata.data
[47] | 134548092| 0|OBJT |LOCL |0 |15 |Drodata.rodata
[43] | 134548092| 0|NOTY |LOCL |0 |15 |Drodata.rodata
[52] | 134613676| 0|OBJT |GLOB |0 |18 |_DYNAMIC
[31] | 134614072| 0|OBJT |LOCL |0 |21 |_END_
[61] | 134613640| 0|OBJT |GLOB |3 |17 |_GLOBAL_OFFSET_TABLE_
[51] | 134547552| 0|OBJT |GLOB |0 |11 |_PROCEDURE_LINKAGE_TABLE_
[32] | 134545408| 0|OBJT |LOCL |0 |1 |_START_
[74] | 0| 0|NOTY |WEAK |0 |UNDEF |__1cG__CrunMdo_exit_code6F_v_
[53] | 134614024| 4|OBJT |GLOB |0 |19 |___Argv
[37] | 134614032| 4|OBJT |LOCL |0 |19 |__do_exit_code_ptr
[72] | 134614000| 24|OBJT |GLOB |0 |19 |__environ_lock
[64] | 134547568| 0|FUNC |GLOB |0 |UNDEF |__fpstart
[66] | 134547808| 123|FUNC |GLOB |0 |12 |__fsr
[57] | 0| 0|NOTY |GLOB |0 |ABS |__fsr_init_value
[36] | 134614028| 4|OBJT |LOCL |0 |19 |__get_exit_frame_monitor_ptr
[73] | 134614036| 4|OBJT |GLOB |0 |19 |__longdouble_used
[54] | 134614072| 0|OBJT |GLOB |0 |20 |_edata
[69] | 134614072| 0|OBJT |GLOB |0 |21 |_end
[59] | 134613996| 4|OBJT |GLOB |0 |19 |_environ
[56] | 134548103| 0|OBJT |GLOB |0 |16 |_etext
[63] | 134547616| 0|FUNC |GLOB |0 |UNDEF |_exit
[71] | 134548064| 27|FUNC |GLOB |0 |14 |_fini
[68] | 134547648| 0|FUNC |WEAK |0 |UNDEF |_get_exit_frame_monitor
[55] | 134548036| 27|FUNC |GLOB |0 |13 |_init
[62] | 134548092| 4|OBJT |GLOB |0 |15 |_lib_version
[70] | 134547664| 142|FUNC |GLOB |0 |12 |_start
[65] | 134547600| 0|FUNC |GLOB |0 |UNDEF |atexit
[34] | 0| 0|FILE |LOCL |0 |ABS |crt1.o
[35] | 0| 0|FILE |LOCL |0 |ABS |crt1.s
[33] | 0| 0|FILE |LOCL |0 |ABS |crti.s
[48] | 0| 0|FILE |LOCL |0 |ABS |crtn.s
[50] | 134613996| 4|OBJT |WEAK |0 |19 |environ
[67] | 134547584| 0|FUNC |GLOB |0 |UNDEF |exit
[38] | 0| 0|FILE |LOCL |0 |ABS |fsr.s
[1] | 0| 0|FILE |LOCL |0 |ABS |hallo
[44] | 0| 0|FILE |LOCL |0 |ABS |hallo.c
[58] | 134548000| 33|FUNC |GLOB |0 |12 |main
[60] | 134547936| 60|FUNC |GLOB |0 |12 |printHallo
[49] | 134547632| 0|FUNC |GLOB |0 |UNDEF |printf
[39] | 134614040| 32|OBJT |LOCL |0 |19 |trap_table
[40] | 0| 0|FILE |LOCL |0 |ABS |values-Xa.c
However, dissecting the same binary got by compiling the source with a C++ compiler yields a different picture.
hallo:
[Index] Value Size Type Bind Other Shndx Name
[3] | 134545672| 0|SECT |LOCL |0 |2 |
[31] | 0| 0|SECT |LOCL |0 |30 |
[30] | 0| 0|SECT |LOCL |0 |29 |
[2] | 134545652| 0|SECT |LOCL |0 |1 |
[5] | 134545948| 0|SECT |LOCL |0 |4 |
[4] | 134545688| 0|SECT |LOCL |0 |3 |
[7] | 134546668| 0|SECT |LOCL |0 |6 |
[6] | 134546156| 0|SECT |LOCL |0 |5 |
[9] | 134547992| 0|SECT |LOCL |0 |8 |
[8] | 134547880| 0|SECT |LOCL |0 |7 |
[11] | 134548148| 0|SECT |LOCL |0 |10 |
[10] | 134548052| 0|SECT |LOCL |0 |9 |
[13] | 134548740| 0|SECT |LOCL |0 |12 |
[12] | 134548368| 0|SECT |LOCL |0 |11 |
[15] | 134549036| 0|SECT |LOCL |0 |14 |
[14] | 134548932| 0|SECT |LOCL |0 |13 |
[17] | 134614656| 0|SECT |LOCL |0 |16 |
[16] | 134614584| 0|SECT |LOCL |0 |15 |
[19] | 134615072| 0|SECT |LOCL |0 |18 |
[18] | 134615016| 0|SECT |LOCL |0 |17 |
[21] | 134615088| 0|SECT |LOCL |0 |20 |
[20] | 134615084| 0|SECT |LOCL |0 |19 |
[23] | 134615164| 0|SECT |LOCL |0 |22 |
[22] | 134615164| 0|SECT |LOCL |0 |21 |
[25] | 0| 0|SECT |LOCL |0 |24 |
[24] | 0| 0|SECT |LOCL |0 |23 |
[27] | 0| 0|SECT |LOCL |0 |26 |
[26] | 0| 0|SECT |LOCL |0 |25 |
[29] | 0| 0|SECT |LOCL |0 |28 |
[28] | 0| 0|SECT |LOCL |0 |27 |
[50] | 134615164| 0|NOTY |LOCL |0 |22 |Bbss.bss
[35] | 0| 0|FILE |LOCL |0 |ABS |CCrti.s
[53] | 0| 0|FILE |LOCL |0 |ABS |CCrtn.s
[49] | 134615088| 0|NOTY |LOCL |0 |20 |Ddata.data
[51] | 134549036| 0|NOTY |LOCL |0 |14 |Drodata.rodata
[64] | 134614656| 0|OBJT |GLOB |0 |16 |_DYNAMIC
[32] | 134615164| 0|OBJT |LOCL |0 |22 |_END_
[70] | 134614584| 0|OBJT |GLOB |3 |15 |_GLOBAL_OFFSET_TABLE_
[60] | 134548148| 0|OBJT |GLOB |0 |10 |_PROCEDURE_LINKAGE_TABLE_
[33] | 134545408| 0|OBJT |LOCL |0 |1 |_START_
[66] | 134548340| 0|FUNC |WEAK |0 |UNDEF |__1cG__CrunMdo_exit_code6F_v_
[80] | 134548324| 0|FUNC |WEAK |0 |UNDEF |__1cG__CrunVdo_exit_code_in_range6Fpv1_v_
[78] | 134548276| 0|FUNC |WEAK |0 |UNDEF |__1cH__CimplKcplus_fini6F_v_
[77] | 134548260| 0|FUNC |GLOB |0 |UNDEF |__1cH__CimplKcplus_init6F_v_
[69] | 134548640| 54|FUNC |GLOB |0 |11 |__1cKprintHallo6Fi_v_
[68] | 134615116| 4|OBJT |GLOB |0 |20 |___Argv
[38] | 134548800| 0|FUNC |LOCL |0 |12 |__cplus_fini_at_exit
[45] | 134615124| 4|OBJT |LOCL |0 |20 |__do_exit_code_ptr
[87] | 134615092| 24|OBJT |GLOB |0 |20 |__environ_lock
[36] | 134548764| 0|FUNC |LOCL |0 |12 |__ex_deregister_at_exit
[85] | 134548164| 0|FUNC |GLOB |0 |UNDEF |__fpstart
[84] | 134548512| 123|FUNC |GLOB |0 |11 |__fsr
[81] | 0| 0|NOTY |GLOB |0 |ABS |__fsr_init_value
[44] | 134615120| 4|OBJT |LOCL |0 |20 |__get_exit_frame_monitor_ptr
[83] | 134615128| 4|OBJT |GLOB |0 |20 |__longdouble_used
[39] | 134615076| 0|NOTY |LOCL |0 |18 |_cpp_finidata0
[71] | 134615164| 0|OBJT |GLOB |0 |21 |_edata
[67] | 134615164| 0|OBJT |GLOB |0 |22 |_end
[73] | 134615088| 4|OBJT |GLOB |0 |20 |_environ
[65] | 134549047| 0|OBJT |GLOB |0 |14 |_etext
[79] | 134548308| 0|FUNC |WEAK |0 |UNDEF |_ex_deregister
[41] | 134615084| 0|NOTY |LOCL |0 |19 |_ex_range0
[56] | 134615084| 0|NOTY |LOCL |0 |19 |_ex_range1
[74] | 134548292| 0|FUNC |WEAK |0 |UNDEF |_ex_register
[37] | 134615048| 0|NOTY |LOCL |0 |17 |_ex_shared0
[54] | 134615064| 0|NOTY |LOCL |0 |17 |_ex_shared1
[40] | 134548368| 0|NOTY |LOCL |0 |11 |_ex_text0
[55] | 134548737| 0|NOTY |LOCL |0 |11 |_ex_text1
[62] | 134548212| 0|FUNC |GLOB |0 |UNDEF |_exit
[75] | 134548932| 101|FUNC |GLOB |0 |13 |_fini
[82] | 134548244| 0|FUNC |WEAK |0 |UNDEF |_get_exit_frame_monitor
[58] | 134548740| 190|FUNC |GLOB |0 |12 |_init
[59] | 134549036| 4|OBJT |GLOB |0 |14 |_lib_version
[61] | 134548368| 142|FUNC |GLOB |0 |11 |_start
[76] | 134548196| 0|FUNC |GLOB |0 |UNDEF |atexit
[42] | 0| 0|FILE |LOCL |0 |ABS |crt1.o
[43] | 0| 0|FILE |LOCL |0 |ABS |crt1.s
[34] | 0| 0|FILE |LOCL |0 |ABS |crti.s
[57] | 0| 0|FILE |LOCL |0 |ABS |crtn.s
[72] | 134615088| 4|OBJT |WEAK |0 |20 |environ
[88] | 134548180| 0|FUNC |GLOB |0 |UNDEF |exit
[46] | 0| 0|FILE |LOCL |0 |ABS |fsr.s
[1] | 0| 0|FILE |LOCL |0 |ABS |hallo
[52] | 0| 0|FILE |LOCL |0 |ABS |hallo.c
[86] | 134548704| 33|FUNC |GLOB |0 |11 |main
[63] | 134548228| 0|FUNC |GLOB |0 |UNDEF |printf
[47] | 134615132| 32|OBJT |LOCL |0 |20 |trap_table
[48] | 0| 0|FILE |LOCL |0 |ABS |values-Xa.c
Observe the function, printHallo(int), that we created. Though the behaviour is the same, the C++ compiler flattens the namespace from printHallo to __1cKprintHallo6Fi_v_ . This behaviour is called the namespace mangling. This way, the C++ compiler provides a consistent flat namespace that UNIX could understand.
Compounding the problems, DTrace is a C-friendly language, which means that we have to exploit this behaviour for applications created with C++. Furthermore, DTrace expects a C-style linkage when you are incorporating the DTrace probes in your application. The first approach would be to declare a C-linkage using, extern "C". This is the natural logic that would come to anyone! However, the C++ compiler would shout an error stating that the
linkage declarations to be specified only at the top-level scope. One way to get around this is to override the default declaration and by specifying the linkage to be C-compliant, before it
is used. This way, one could remove the internal declaration as you could read here.
DTrace has a rich set of flags and we could use these flags to bring about this internally. How to get this done. I have written 2 sample programs, based on the listings here. The programs simulate a client-server methodology, but in our example, we shall use the shared memory communication as out test program. The server listing from the above URL has been modified to be a C++ program. I shall call this program, server.cpp. The listing is as follows:
server.cpp:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#define SHMSZ 27
class Server{
private:
char c;
int shmid;
key_t key;
char *shm, *s;
public:
Server();
key_t getKey(void);
void setKey(key_t);
char getShm(void);
bool createSegment();
bool attachSegment();
void populateSegment();
void run();
virtual ~Server();
};
Server::Server(){
}
char Server::getShm(void){
return *shm;
}
key_t Server::getKey(){
return key;
}
void Server::setKey(key_t shkey){
key = shkey;
}
bool Server::createSegment(){
if ((shmid = shmget(key, SHMSZ, IPC_CREAT | 0666)) < 0) {
return false;
} else{
return true;
}
}
bool Server::attachSegment(){
if ((shm = (char *)shmat(shmid, NULL, 0)) == (char *) -1) {
return false;
} else{
return true;
}
}
void Server::populateSegment(){
s = shm;
for (c = 'a'; c <= 'z'; c++)
*s++ = c;
*s = NULL;
}
void Server::run(){
while (*shm != '*'){};
printf("Finished running"
;
}
Server::~Server(){
s = shm;
while(*s !='z'){
*s++ = NULL;
}
shm_unlink(shm);
shm = NULL;
}
int main(void){
Server *server = new Server();
server->setKey(0x1234);
server->createSegment();
server->attachSegment();
server->populateSegment();
server->run();
delete(server);
server = NULL;
return 0;
}
As you could observe from the listing, we have 3 methods : createSegment, attachSegment and populateSegment, which are of interest to us. So, let us create a provider script that contains this information. I shall call this probes.d .The listing is as follows:
probes.d:
provider server{
probe createSegment();
probe attachSegment();
probe populateSegment();
probe run();
};
Let us study this script. The first line : provider server, means that the provider has the name, "server". This is also to comply with the program which we wish to trace. The following lines contain the methods from the program, which are the points of interest to us. Also, note that the methods take in no arguments, as is in the source listing from server.cpp. If you have methods which accept arguments, you should make sure that the entries in the provider script match the arguments exactly.
The next step is to compile this script and to generate a header file that provides the interface between the script and the C++ program. We shall do this as follows:
meson:/export/home/mithun/USDT >dtrace -h -s probes.d
This generates the header file, probes.h. The content of this header file looks like this.
probes.h:
/*
* Generated by dtrace(1M).
*/
#ifndef _PROBES_H
#define _PROBES_H
#include <unistd.h>
#ifdef __cplusplus
extern "C" {
#endif
#if _DTRACE_VERSION
#define SERVER_ATTACHSEGMENT() \
__dtrace_server___attachSegment()
#define SERVER_ATTACHSEGMENT_ENABLED() \
__dtraceenabled_server___attachSegment()
#define SERVER_CREATESEGMENT() \
__dtrace_server___createSegment()
#define SERVER_CREATESEGMENT_ENABLED() \
__dtraceenabled_server___createSegment()
#define SERVER_POPULATESEGMENT() \
__dtrace_server___populateSegment()
#define SERVER_POPULATESEGMENT_ENABLED() \
__dtraceenabled_server___populateSegment()
#define SERVER_RUN() \
__dtrace_server___run()
#define SERVER_RUN_ENABLED() \
__dtraceenabled_server___run()
extern void __dtrace_server___attachSegment(void);
extern int __dtraceenabled_server___attachSegment(void);
extern void __dtrace_server___createSegment(void);
extern int __dtraceenabled_server___createSegment(void);
extern void __dtrace_server___populateSegment(void);
extern int __dtraceenabled_server___populateSegment(void);
extern void __dtrace_server___run(void);
extern int __dtraceenabled_server___run(void);
#else
#define SERVER_ATTACHSEGMENT()
#define SERVER_ATTACHSEGMENT_ENABLED() (0)
#define SERVER_CREATESEGMENT()
#define SERVER_CREATESEGMENT_ENABLED() (0)
#define SERVER_POPULATESEGMENT()
#define SERVER_POPULATESEGMENT_ENABLED() (0)
#define SERVER_RUN()
#define SERVER_RUN_ENABLED() (0)
#endif
#ifdef __cplusplus
}
#endif
#endif /* _PROBES_H */
As you could observe, the header file also carries the linkage information at the very begining.
#ifdef __cplusplus
extern "C" {
#endif
Also, note that the function names have been generated, such as : SERVER_ATTACHSEGMENT(), SERVER_POPULATESEGMENT() & SERVER_CREATESEGMENT() with additional functions, carrying the suffix _ENABLED, to these functions, like SERVER_ATTACHSEGMENT()_ENABLED. Also, note that these functions have a flat namespace, which makes UNIX and DTrace very happy!
That we now have the flat function names, the only task remaining it to make the application aware of the probes provided by the probes.d script. We shall incorporate this information manually, as of now. There are also means to consolidate this information in a shared object and dynamically load and use the probes, as is done by the dtrace.so extension in php. I shall be re-visiting this later.
The probes could be inserted at the appropriate locations as could be seen from the modified server.cpp file.
server.cpp : (modified with probes)
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include "probes.h"
#define SHMSZ 27
class Server{
private:
char c;
int shmid;
key_t key;
char *shm, *s;
public:
Server();
key_t getKey(void);
void setKey(key_t);
char getShm(void);
bool createSegment();
bool attachSegment();
void populateSegment();
void run();
virtual ~Server();
};
Server::Server(){
}
char Server::getShm(void){
return *shm;
}
key_t Server::getKey(){
return key;
}
void Server::setKey(key_t shkey){
key = shkey;
}
bool Server::createSegment(){
SERVER_CREATESEGMENT();
if ((shmid = shmget(key, SHMSZ, IPC_CREAT | 0666)) < 0) {
return false;
} else{
return true;
}
}
bool Server::attachSegment(){
SERVER_ATTACHSEGMENT();
if ((shm = (char *)shmat(shmid, NULL, 0)) == (char *) -1) {
return false;
} else{
return true;
}
}
void Server::populateSegment(){
SERVER_POPULATESEGMENT();
s = shm;
for (c = 'a'; c <= 'z'; c++)
*s++ = c;
*s = NULL;
}
void Server::run(){
while (*shm != '*'){};
printf("Finished running"
;
}
Server::~Server(){
s = shm;
while(*s !='z'){
*s++ = NULL;
}
shm_unlink(shm);
shm = NULL;
}
int main(void){
Server *server = new Server();
server->setKey(0x1234);
server->createSegment();
server->attachSegment();
server->populateSegment();
server->run();
delete(server);
server = NULL;
return 0;
}
Observe that I have included the header file, "probes.h" in this C++ code. Now, compile the C++ code as follows:
meson:/export/home/mithun/USDT >CC -c server.cpp
The source compiles fine and we're now ready to move on to the next step, which is to link the DTrace script and the object file generated from the server.cpp source file, for which we shall be using the DTrace command line interpreter. We shall do this as follows:
meson:/export/home/mithun/USDT >dtrace -G -s probes.d server.o
This compiles fine as well and we are ready to do the last action item : to generate the executable containing the DTrace probes built in. This is just trivial task and shall be done by issuing:
meson:/export/home/mithun/USDT >CC -o server probes.o server.o
This is a good thing as we now have the added benefit of exploiting the compiler's type checking. This is because, the macros in the generated
header file require the types specified in the provider definition. Thats it! We're now ready to launch the application. However, we need the client side, which is just for the demonstration purposes. The client program is just copied from the URL I provided earlier, with some very minor modifications. The listing, client.c is as follows:
client.c:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <stdlib.h>
#define SHMSZ 27
int main(void){
int shmid;
key_t key;
char *shm, *s;
/*
* We need to get the segment named
* "5678", created by the server.
*/
key = 0x1234;
/*
* Locate the segment.
*/
if ((shmid = shmget(key, SHMSZ, 0666)) < 0) {
perror("shmget"
;
exit(1);
}
/*
* Now we attach the segment to our data space.
*/
if ((shm = (char*)shmat(shmid, NULL, 0)) == (char *) -1) {
perror("shmat"
;
exit(1);
}
/*
* Now read what the server put in the memory.
*/
for (s = shm; *s != NULL; s++)
putchar(*s);
putchar('\n');
/*
* Finally, change the first character of the
* segment to '*', indicating we have read
* the segment.
*/
*shm = '*';
return 0;
}
I shall compile this file as follows:
meson:/export/home/mithun/USDT >CC client.c -o client
I, now have, the entire topology ready. I shall start the demo as follows:
meson:/export/home/mithun/USDT >dtrace -ZP server'$target' -c ./server
dtrace: description 'server$target' matched 3 probes
CPU ID FUNCTION:NAME
0 88376 __1cGServerNcreateSegment6M_b_:createSegment
0 88375 __1cGServerNattachSegment6M_b_:attachSegment
0 88377 __1cGServerPpopulateSegment6M_v_:populateSegment
You could also see the output from the run. I am using an one-liner here, which is a very famous DTrace feature. Its pithiness! Here, I am enabling the DTrace provider, "server", which is from out DTrace script. I am matching this with the process ID of the application, which I invoke with the -c flag followed by the program name towards the end of the command. I also get the information about the probes that matched almost immediately.
It also makes sense to see the structure of the individual object files. I shall not be investigating this here, but its worthwhile to have a look at the contents of the object files : probes.o ,server.o and server. You shall be able to observe that the probes are actually provided by the probes.o object file, which is obtained by the compilation of the script at step-3.
Now, you're better off at introducing DTrace probes to your applications. You could download the complete set of examples here.Posted at 05:06PM Nov 12, 2007 by mithun in DTrace | Comments[0]