Update copyright to LGPL on all files
[dyninst.git] / dyninstAPI / tests / src / test13.C
1 /*
2  * Copyright (c) 1996-2009 Barton P. Miller
3  * 
4  * We provide the Paradyn Parallel Performance Tools (below
5  * described as "Paradyn") on an AS IS basis, and do not warrant its
6  * validity or performance.  We reserve the right to update, modify,
7  * or discontinue this software at any time.  We shall have no
8  * obligation to supply such updates or modifications or any other
9  * form of support to you.
10  * 
11  * By your use of Paradyn, you understand and agree that we (or any
12  * other person or entity with proprietary rights in Paradyn) are
13  * under no obligation to provide either maintenance services,
14  * update services, notices of latent defects, or correction of
15  * defects for Paradyn.
16  * 
17  * This library is free software; you can redistribute it and/or
18  * modify it under the terms of the GNU Lesser General Public
19  * License as published by the Free Software Foundation; either
20  * version 2.1 of the License, or (at your option) any later version.
21  * 
22  * This library is distributed in the hope that it will be useful,
23  * but WITHOUT ANY WARRANTY; without even the implied warranty of
24  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
25  * Lesser General Public License for more details.
26  * 
27  * You should have received a copy of the GNU Lesser General Public
28  * License along with this library; if not, write to the Free Software
29  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
30  */
31
32 #include <BPatch.h>
33 #include <BPatch_process.h>
34 #include <BPatch_thread.h>
35 #include "test_util.h"
36
37 #define NUM_THREADS 5
38 #define TIMEOUT 20
39
40 BPatch bpatch;
41 BPatch_process *proc;
42 unsigned error = 0;
43 bool create_proc = true;
44 unsigned thread_count;
45 static char dyn_tids[NUM_THREADS];
46 static char deleted_tids[NUM_THREADS];
47 // We can get extra threads; add a layer of indirection. Yay.
48 static int our_tid_max = 0;
49 static char thread_mapping[NUM_THREADS];
50
51 static int deleted_threads;
52
53 bool debug_flag = false;
54 #define dprintf if (debug_flag) fprintf
55 #define NUM_FUNCS 6 
56 char initial_funcs[NUM_FUNCS][25] = {"init_func", "main", "_start", "__start", "__libc_start_main", "_lwp_start"};
57
58 int bpindex_to_myindex(int index) {
59     for (unsigned i = 0; i < our_tid_max; i++) {
60         if (thread_mapping[i] == index) return i;
61     }
62     return -1;
63 }
64
65 void deadthr(BPatch_process *my_proc, BPatch_thread *thr)
66 {
67    dprintf(stderr, "%s[%d]:  welcome to deadthr\n", __FILE__, __LINE__);
68    if (!thr) {
69      dprintf(stderr, "%s[%d]:  deadthr called without valid ptr to thr\n",
70             __FILE__, __LINE__);
71      return;
72    }
73    unsigned my_dyn_id = bpindex_to_myindex(thr->getBPatchID());
74    if (my_dyn_id == -1) return;
75
76    if (my_proc != proc)
77    {
78        fprintf(stderr, "[%s:%u] - Got invalid process: %p vs %p\n", __FILE__, __LINE__, my_proc, proc);
79       error = 1;
80    }
81    deleted_tids[my_dyn_id] = 1;
82    deleted_threads++;
83    dprintf(stderr, "%s[%d]:  leaving deadthr\n", __FILE__, __LINE__);
84 }
85
86 void newthr(BPatch_process *my_proc, BPatch_thread *thr)
87 {
88    dprintf(stderr, "%s[%d]:  welcome to newthr, error = %d\n", __FILE__, __LINE__, error);
89
90    if (create_proc && proc && (my_proc != proc) )
91    {
92       fprintf(stderr, "[%s:%u] - Got invalid process: %p vs %p\n", 
93               __FILE__, __LINE__, my_proc, proc);
94       error = 1;
95    }
96
97    if (thr->isDeadOnArrival()) {
98       fprintf(stderr, "[%s:%u] - Got a dead on arival thread\n", 
99               __FILE__, __LINE__);
100       error = 1;
101       return;
102    }
103
104    dprintf(stderr, "%s[%d]:  newthr: BPatchID = %d\n", __FILE__, __LINE__, thr->getBPatchID());
105    //Check initial function
106    static char name[1024];
107    BPatch_function *f = thr->getInitialFunc();   
108    if (f) f->getName(name, 1024);
109    else strcpy(name, "<NONE>");
110
111    int found_name = 0;
112    for (unsigned i=0; i<NUM_FUNCS; i++)
113       if (!strcmp(name, initial_funcs[i]))
114       {
115          found_name = 1;
116          break;
117       }
118    dprintf(stderr, "%s[%d]:  newthr: %s\n", __FILE__, __LINE__, name);
119    if (!found_name)
120    {
121        // We can get unexpected threads with different initial functions; do not include
122        // them (but don't consider it an error). If we don't walk the stack right, then
123        // we won't have enough expected threads and so check it later.
124       fprintf(stderr, "[%s:%d] - Thread %d has unexpected initial function '%s'; ignoring\n",
125               __FILE__, __LINE__, thr->getBPatchID(), name);
126       return;
127    }
128
129    if (bpindex_to_myindex(thr->getBPatchID()) != -1) {
130       fprintf(stderr, "[%s:%d] - WARNING: Thread %d called in callback twice\n",
131               __FILE__, __LINE__, thr->getBPatchID());
132       return;
133    }
134
135    unsigned my_dyn_id = our_tid_max; our_tid_max++;
136
137    thread_mapping[my_dyn_id] = thr->getBPatchID();
138
139    //Stacks should be unique and non-zero
140    static unsigned long stack_addrs[NUM_THREADS];
141    unsigned long my_stack = thr->getStackTopAddr();
142    if (!my_stack)
143    {
144       fprintf(stderr, "[%s:%d] - WARNING: Thread %d has no stack\n",
145               __FILE__, __LINE__, my_dyn_id);
146       error = 1;
147    }
148    else
149    {
150       for (unsigned i=0; i<NUM_THREADS; i++)
151          if (stack_addrs[i] == my_stack)
152          {
153             fprintf(stderr, "[%s:%d] - WARNING: Thread %d and %d share a stack at %lx\n",
154                     __FILE__, __LINE__, my_dyn_id, i, my_stack);
155             error = 1;
156          }
157    }
158    stack_addrs[my_dyn_id] = my_stack;
159
160    //Thread IDs should be unique
161    static long pthread_ids[NUM_THREADS];
162    long mytid = thr->getTid();
163    if (mytid == -1)
164    {
165       fprintf(stderr, "[%s:%d] - WARNING: Thread %d has a tid of -1\n", 
166               __FILE__, __LINE__, my_dyn_id);
167    }
168    dprintf(stderr, "%s[%d]:  newthr: tid = %lu\n", 
169            __FILE__, __LINE__,  (unsigned long)mytid);
170    for (unsigned i=0; i<NUM_THREADS; i++)
171       if (i != my_dyn_id && dyn_tids[i] && mytid == pthread_ids[i])
172       {
173             fprintf(stderr, "[%s:%d] - WARNING: Thread %d and %d share a tid of %ld\n",
174                     __FILE__, __LINE__, my_dyn_id, i, mytid);
175             error = 1;
176       }
177    pthread_ids[my_dyn_id] = mytid;
178
179    thread_count++;
180    dyn_tids[my_dyn_id] = 1;
181    dprintf(stderr, "%s[%d]:  leaving newthr: error = %d\n", __FILE__, __LINE__, error);
182 }
183
184 void upgrade_mutatee_state()
185 {
186    dprintf(stderr, "%s[%d]:  welcome to upgrade_mutatee_state\n", __FILE__, __LINE__);
187    BPatch_variableExpr *var;
188    BPatch_constExpr *one;
189    BPatch_arithExpr *inc_var;
190    BPatch_arithExpr *inc_var_assign;
191
192    BPatch_image *img = proc->getImage();
193    var = img->findVariable("proc_current_state");
194    one = new BPatch_constExpr(1);
195    inc_var = new BPatch_arithExpr(BPatch_plus, *var, *one);
196    inc_var_assign = new BPatch_arithExpr(BPatch_assign, *var, *inc_var);
197    dprintf(stderr, "%s[%d]: going into oneTimecode...\n", __FILE__, __LINE__);
198    proc->oneTimeCode(*inc_var_assign);
199    dprintf(stderr, "%s[%d]:  upgrade_mutatee_state: after oneTimeCode\n", __FILE__, __LINE__);
200 }
201
202 #define MAX_ARGS 32
203 char *filename = "test13.mutatee_gcc";
204 char *args[MAX_ARGS];
205 char *create_arg = "-create";
206 unsigned num_args = 0; 
207
208 static BPatch_process *getProcess()
209 {
210    args[0] = filename;
211
212    BPatch_process *proc;
213    if (create_proc) {
214       args[1] = create_arg;
215       proc = bpatch.processCreate(filename, (const char **) args);
216       if(proc == NULL) {
217          fprintf(stderr, "%s[%d]: processCreate(%s) failed\n", 
218                  __FILE__, __LINE__, filename);
219          return NULL;
220       }
221    }
222    else
223    {
224       dprintf(stderr, "%s[%d]: starting process for attach\n", __FILE__, __LINE__);
225       int pid = startNewProcessForAttach(filename, (const char **) args);
226       if (pid < 0)
227       {
228          fprintf(stderr, "%s ", filename);
229          perror("couldn't be started");
230          return NULL;
231       }
232       dprintf(stderr, "%s[%d]: started process, now attaching\n", __FILE__, __LINE__);
233       proc = bpatch.processAttach(filename, pid);
234       if(proc == NULL) {
235          fprintf(stderr, "%s[%d]: processAttach(%s, %d) failed\n", 
236                  __FILE__, __LINE__, filename, pid);
237          return NULL;
238       }
239       dprintf(stderr, "%s[%d]: attached to process\n", __FILE__, __LINE__);
240       BPatch_image *appimg = proc->getImage();
241       signalAttached(NULL, appimg);
242    }
243    return proc;
244 }
245
246 char libRTname[256];
247
248 static void parse_args(unsigned argc, char *argv[])
249 {
250    unsigned i;
251    args[0] = NULL;
252    for (i=1; i<argc; i++)
253    {
254       if (strcmp(argv[i], "-attach") == 0)
255       {
256          create_proc = false;
257       }
258       else if (strcmp(argv[i], "-mutatee") == 0)
259       {
260          if (++i == argc) break;
261          filename = argv[i];
262       }
263       else if (strcmp(argv[i], "-verbose") == 0)
264           debug_flag = true;
265       else if (!strcmp(argv[i], "-V")) {
266          if (libRTname[0]) {
267             fprintf (stdout, "DYNINSTAPI_RT_LIB=%s\n", libRTname);
268             fflush(stdout);
269          }
270       }
271       else {
272           fprintf(stderr, "Usage: test13 [-V] [-verbose] [-attach]|[-mutatee <file>]\n");
273           exit(-1);
274       }
275    }
276 }
277
278 int main(int argc, char *argv[])
279 {
280    unsigned num_attempts = 0;
281    bool missing_threads = false;
282
283    libRTname[0]='\0';
284    if (!getenv("DYNINSTAPI_RT_LIB")) {
285          fprintf(stderr,"Environment variable DYNINSTAPI_RT_LIB undefined:\n"
286 #if defined(i386_unknown_nt4_0)
287                  "    using standard search strategy for libdyninstAPI_RT.dll\n");
288 #else
289                  "    set it to the full pathname of libdyninstAPI_RT\n");
290          exit(-1);
291 #endif
292     } else
293          strcpy((char *)libRTname, (char *)getenv("DYNINSTAPI_RT_LIB"));
294
295    updateSearchPaths(argv[0]);
296
297    parse_args(argc, argv);
298
299    bpatch.registerThreadEventCallback(BPatch_threadCreateEvent, newthr);
300    bpatch.registerThreadEventCallback(BPatch_threadDestroyEvent, deadthr);
301
302    proc = getProcess();
303    if (!proc)
304       return -1;
305
306    proc->continueExecution();
307
308    // Wait for NUM_THREADS new thread callbacks to run
309    while (thread_count < NUM_THREADS) {
310        dprintf(stderr, "Going into waitForStatusChange; thread count %d, NUM_THREADS %d...\n", thread_count, NUM_THREADS);
311       bpatch.waitForStatusChange();
312       dprintf(stderr, "Back from waitForStatusChange...\n");
313       if (proc->isTerminated()) {
314          fprintf(stderr, "[%s:%d] - App exited early\n", __FILE__, __LINE__);
315          error = 1;
316          break;
317       }
318       if (num_attempts++ == TIMEOUT) {
319          fprintf(stderr, "[%s:%d] - Timed out waiting for threads\n", 
320                  __FILE__, __LINE__);
321          fprintf(stderr, "[%s:%d] - Only have %u threads, expected %u!\n",
322                  __FILE__, __LINE__, thread_count, NUM_THREADS);
323          error = 1;
324          break;
325       }
326       P_sleep(1);
327    }
328
329    dprintf(stderr, "%s[%d]:  done waiting for thread creations, error = %d\n", __FILE__, __LINE__, error);
330
331    BPatch_Vector<BPatch_thread *> thrds;
332    proc->getThreads(thrds);
333
334    if (thrds.size() < NUM_THREADS)
335    {
336       fprintf(stderr, "[%s:%d] - Have %u threads, expected %u!\n",
337               __FILE__, __LINE__, thrds.size(), NUM_THREADS);
338       error = 1;
339    }
340
341    for (unsigned i=0; i<NUM_THREADS; i++)
342    {
343       if (!dyn_tids[i])
344       {
345          fprintf(stderr, "[%s:%d] - Thread %u was never created!\n",
346                  __FILE__, __LINE__, i);
347          missing_threads = true;
348       }
349    }
350
351    if(error || missing_threads) {
352       fprintf(stderr, "%s[%d]: ERROR during thread create stage, exiting\n", __FILE__, __LINE__);
353       printf("*** Failed test #1 (Threading Callbacks)\n");
354       if(proc && !proc->isTerminated())
355          proc->terminateExecution();
356       return -1;
357    }
358
359    // Update state to allow threads to exit
360    upgrade_mutatee_state();
361
362    dprintf(stderr, "%s[%d]:  Now waiting for application to terminate.\n", __FILE__, __LINE__);
363
364    while (!proc->isTerminated())
365       bpatch.waitForStatusChange();
366
367    num_attempts = 0; 
368    while(deleted_threads != NUM_THREADS && num_attempts != TIMEOUT) {
369       num_attempts++;
370       P_sleep(1);
371    }
372
373    for (unsigned i=1; i<NUM_THREADS; i++)
374    {
375       if (!deleted_tids[i])
376       {
377          fprintf(stderr, "[%s:%d] - Thread %d wasn't deleted\n",
378                  __FILE__, __LINE__, i);
379          error = 1;
380       }
381    }
382
383    if (deleted_threads != NUM_THREADS || !deleted_tids[0])
384    {
385       fprintf(stderr, "[%s:%d] - %d threads deleted at termination." 
386            "  Expected %d\n", __FILE__, __LINE__, deleted_threads, NUM_THREADS);
387       error = 1;
388    }
389
390    if (error)
391    {
392        printf("*** Failed test #1 (Threading Callbacks)\n");
393    } else {
394        printf("Passed test #1 (Threading Callbacks)\n");
395        printf("All tests passed.\n");
396        return 0;
397    }
398    return -1;
399 }