Update copyright to LGPL on all files
[dyninst.git] / testsuite / src / dyninst / test_thread_6.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 <BPatch_function.h>
36 #include "test_lib.h"
37
38 #include "dyninst_comp.h"
39 class test_thread_6_Mutator : public DyninstMutator {
40 protected:
41   char *logfilename;
42   BPatch *bpatch;
43   bool create_proc;
44
45   void upgrade_mutatee_state();
46   BPatch_process *getProcess();
47   test_results_t mutatorTest(BPatch *bpatch);
48
49 public:
50   test_thread_6_Mutator();
51   virtual bool hasCustomExecutionPath() { return true; }
52   virtual test_results_t setup(ParameterDict &param);
53   virtual test_results_t executeTest();
54 };
55 extern "C" DLLEXPORT TestMutator *test_thread_6_factory() {
56   return new test_thread_6_Mutator();
57 }
58
59 test_thread_6_Mutator::test_thread_6_Mutator()
60   : logfilename(NULL), bpatch(NULL), create_proc(true) {
61 }
62
63 #define NUM_THREADS 5
64 #define TIMEOUT 20
65
66 static BPatch_process *proc;
67 static unsigned error13 = 0;
68 static unsigned thread_count;
69 static char dyn_tids[NUM_THREADS];
70 static char deleted_tids[NUM_THREADS];
71 // We can get extra threads; add a layer of indirection. Yay.
72 static int our_tid_max = 0;
73 static int thread_mapping[NUM_THREADS];
74 static int deleted_threads;
75
76 static unsigned long stack_addrs[NUM_THREADS];
77
78 static bool debug_flag = false;
79 #define dprintf if (debug_flag) fprintf
80
81 #define NUM_FUNCS 6
82 static char initial_funcs[NUM_FUNCS][25] = {"init_func", "main", "_start", "__start", "__libc_start_main", "mainCRTStartup"};
83
84 // Globals: our_tid_max, thread_mapping
85 static int bpindex_to_myindex(int index) {
86     for (unsigned i = 0; i < our_tid_max; i++) {
87         if (thread_mapping[i] == index) return i;
88     }
89     return -1;
90 }
91
92 // Globals: deleted_threads, deleted_tids, error13, proc
93 static void deadthr(BPatch_process *my_proc, BPatch_thread *thr)
94 {
95    dprintf(stderr, "%s[%d]:  welcome to deadthr\n", __FILE__, __LINE__);
96    if (!thr) {
97      dprintf(stderr, "%s[%d]:  deadthr called without valid ptr to thr\n",
98             __FILE__, __LINE__);
99      return;
100    }
101    unsigned my_dyn_id = bpindex_to_myindex(thr->getBPatchID());
102    if (-1 == my_dyn_id) {
103       return;
104    }
105
106    if (my_proc != proc)
107    {
108       logerror("[%s:%u] - Got invalid process: %p vs %p\n", __FILE__,
109               __LINE__, my_proc, proc);
110       error13 = 1;
111    }
112    deleted_tids[my_dyn_id] = 1;
113    deleted_threads++;
114    dprintf(stderr, "%s[%d]:  leaving to deadthr, %d is dead\n", __FILE__, __LINE__, my_dyn_id);
115 }
116
117 // Globals: dyn_tids, error13, initial_funcs(?), our_tid_max, proc,
118 // stack_addrs, thread_count, thread_mapping
119 static void newthr(BPatch_process *my_proc, BPatch_thread *thr)
120 {
121    dprintf(stderr, "%s[%d]:  welcome to newthr, error13 = %d\n", __FILE__, __LINE__, error13);
122
123    if (my_proc != proc)
124    {
125       logerror("[%s:%u] - Got invalid process: %p vs %p\n", 
126               __FILE__, __LINE__, my_proc, proc);
127       error13 = 1;
128    }
129
130    if (thr->isDeadOnArrival()) {
131       logerror("[%s:%u] - Got a dead on arival thread\n", 
132               __FILE__, __LINE__);
133       error13 = 1;
134       return;
135    }
136
137    unsigned my_dyn_id = our_tid_max; our_tid_max++;
138    if (bpindex_to_myindex(thr->getBPatchID()) != -1) {
139       logerror("[%s:%d] - WARNING: Thread %d called in callback twice\n",
140               __FILE__, __LINE__, thr->getBPatchID());
141       error13 = 1;
142       return;
143    }
144
145    thread_mapping[my_dyn_id] = thr->getBPatchID();
146    thread_count++;
147    dyn_tids[my_dyn_id] = 1;
148
149    dprintf(stderr, "%s[%d]:  newthr: BPatchID = %d\n", __FILE__, __LINE__, thr->getBPatchID());
150    //Check initial function
151    // BUG(?) Make sure this variable gets initialized properly!
152    static char name[1024];
153    BPatch_function *f = thr->getInitialFunc();   
154    if (f) f->getName(name, 1024);
155    else strcpy(name, "<NONE>");
156
157    int found_name = 0;
158    for (unsigned i=0; i<NUM_FUNCS; i++)
159       if (!strcmp(name, initial_funcs[i]))
160       {
161          found_name = 1;
162          break;
163       }
164    dprintf(stderr, "%s[%d]:  newthr: %s\n", __FILE__, __LINE__, name);
165
166    //Initial thread function detection is proving VERY difficult on Windows,
167    //currently leaving disabled.
168    if (!found_name)
169    {
170        // We can get unexpected threads with different initial functions; do not include
171        // them (but don't consider it an error). If we don't walk the stack right, then
172        // we won't have enough expected threads and so check it later.
173       logerror("[%s:%d] - Thread %d has unexpected initial function '%s'; ignoring\n",
174               __FILE__, __LINE__, thr->getBPatchID(), name);
175       //      error13 = 1; // This shouldn't be an error, according to the comment above.
176       BPatch_Vector<BPatch_frame> stack;
177       thr->getCallStack(stack);
178    }
179
180    //Stacks should be unique and non-zero
181    // Moving this variable to global scope
182    //static unsigned long stack_addrs[NUM_THREADS];
183    unsigned long my_stack = thr->getStackTopAddr();
184    if (!my_stack)
185    {
186       logerror("[%s:%d] - WARNING: Thread %d has no stack\n",
187               __FILE__, __LINE__, my_dyn_id);
188    }
189    else
190    {
191       for (unsigned i=0; i<NUM_THREADS; i++)
192          if (stack_addrs[i] == my_stack)
193          {
194             logerror("[%s:%d] - WARNING: Thread %d and %d share a stack at %lx\n",
195                     __FILE__, __LINE__, my_dyn_id, i, my_stack);
196          }
197    }
198    stack_addrs[my_dyn_id] = my_stack;
199
200    //Thread IDs should be unique
201    // FIXME Make sure this static variable works correctly.  Maybe push it out
202    // to a regular global variable..
203    static long pthread_ids[NUM_THREADS];
204    long mytid = thr->getTid();
205    if (mytid == -1)
206    {
207       logerror("[%s:%d] - WARNING: Thread %d has a tid of -1\n", 
208               __FILE__, __LINE__, my_dyn_id);
209    }
210    dprintf(stderr, "%s[%d]:  newthr: tid = %lu\n", 
211            __FILE__, __LINE__,  (unsigned long)mytid);
212    for (unsigned i=0; i<NUM_THREADS; i++)
213       if (i != my_dyn_id && dyn_tids[i] && mytid == pthread_ids[i])
214       {
215             logerror("[%s:%d] - WARNING: Thread %d and %d share a tid of %lu\n",
216                     __FILE__, __LINE__, my_dyn_id, i, mytid);
217             error13 = 1;
218       }
219    pthread_ids[my_dyn_id] = mytid;
220
221    dprintf(stderr, "%s[%d]:  leaving newthr: error13 = %d\n", __FILE__, __LINE__, error13);
222 }
223
224 void test_thread_6_Mutator::upgrade_mutatee_state()
225 {
226    dprintf(stderr, "%s[%d]:  welcome to upgrade_mutatee_state\n", __FILE__, __LINE__);
227    BPatch_variableExpr *var;
228    BPatch_constExpr *one;
229    BPatch_arithExpr *inc_var;
230    BPatch_arithExpr *inc_var_assign;
231
232    BPatch_image *img = proc->getImage();
233    var = img->findVariable("proc_current_state");
234    one = new BPatch_constExpr(1);
235    inc_var = new BPatch_arithExpr(BPatch_plus, *var, *one);
236    inc_var_assign = new BPatch_arithExpr(BPatch_assign, *var, *inc_var);
237    dprintf(stderr, "%s[%d]: going into oneTimecode...\n", __FILE__, __LINE__);
238    proc->oneTimeCode(*inc_var_assign);
239    dprintf(stderr, "%s[%d]:  upgrade_mutatee_state: after oneTimeCode\n", __FILE__, __LINE__);
240 }
241
242 #define MAX_ARGS 32
243 static char *filename = "test13.mutatee_gcc";
244 static char *args[MAX_ARGS];
245 static char *create_arg = "-create";
246 static unsigned num_args = 0; 
247
248 // This method creates (or attaches to?) the mutatee process and returns a
249 // handle for it
250 BPatch_process *test_thread_6_Mutator::getProcess()
251 {
252   int n = 0;
253    args[n++] = filename;
254
255    if (NULL == logfilename) {
256      args[n++] = "-log";
257      args[n++] = "-";
258    } else  {
259      args[n++] = "-log";
260      args[n++] = logfilename;
261    }
262
263    args[n++] = "-run";
264    args[n++] = "test_thread_6";
265
266    BPatch_process *proc;
267    if (create_proc) {
268       args[n++] = create_arg; // I don't think this does anything.
269       args[n] = NULL;
270       proc = bpatch->processCreate(filename, (const char **) args);
271       if(proc == NULL) {
272          logerror("%s[%d]: processCreate(%s) failed\n", 
273                  __FILE__, __LINE__, filename);
274          return NULL;
275       }
276       // FIXME(?) Is this call thread-safe?
277       registerPID(proc->getPid()); // Register for cleanup
278    }
279    else
280      { // useAttach
281       dprintf(stderr, "%s[%d]: starting process for attach\n",
282               __FILE__, __LINE__);
283       args[n] = NULL;
284       // FIXME figure out what to put for outlog & errlog..
285       int pid = startNewProcessForAttach(filename, (const char **) args,
286                                          getOutputLog(),
287                                          getErrorLog(), true);
288       if (pid < 0) {
289         logerror("%s couldn't be started\n", filename);
290          fprintf(stderr, "%s ", filename);
291          fprintf(stderr, "couldn't be started");
292          return NULL;
293       } else if (pid > 0) {
294         registerPID(pid); // Register for cleanup
295       }
296
297 #if defined(os_windows_test)
298       P_sleep(1);
299 #endif
300
301       dprintf(stderr, "%s[%d]: started process, now attaching\n", __FILE__, __LINE__);
302       fflush(stderr);
303
304       proc = bpatch->processAttach(filename, pid);  
305       if(proc == NULL) {
306          logerror("%s[%d]: processAttach(%s, %d) failed\n", 
307                  __FILE__, __LINE__, filename, pid);
308          return NULL;
309       }
310       BPatch_image *appimg = proc->getImage();
311       signalAttached(NULL, appimg);    
312    }
313    return proc;
314 }
315
316 test_results_t test_thread_6_Mutator::mutatorTest(BPatch *bpatch)
317 {
318    unsigned num_attempts = 0;
319    bool missing_threads = false;
320
321    error13 = 0;
322    thread_count = 0;
323    memset(dyn_tids, 0, sizeof(dyn_tids));
324    memset(deleted_tids, 0, sizeof(deleted_tids));
325    our_tid_max = 0;
326    memset(thread_mapping, -1, sizeof(thread_mapping));
327    deleted_threads = 0;
328    memset(stack_addrs, 0, sizeof(stack_addrs));
329
330    proc = getProcess();
331    if (!proc)
332       return FAILED;
333
334    proc->continueExecution();
335
336    // Wait for NUM_THREADS new thread callbacks to run
337    while (thread_count < NUM_THREADS) {
338       dprintf(stderr, "Going into waitForStatusChange...\n");
339       bpatch->waitForStatusChange();
340       dprintf(stderr, "Back from waitForStatusChange...\n");
341       if (proc->isTerminated())
342       {
343          logerror("[%s:%d] - App exited early\n", __FILE__, __LINE__);
344          error13 = 1;
345          break;
346       }
347       if (num_attempts++ == TIMEOUT)
348       {
349          logerror("[%s:%d] - Timed out waiting for threads\n", 
350                  __FILE__, __LINE__);
351          logerror("[%s:%d] - Only have %u threads, expected %u!\n",
352               __FILE__, __LINE__, thread_count, NUM_THREADS);
353          return FAILED;
354       }
355       P_sleep(1);
356    }
357
358    dprintf(stderr, "%s[%d]:  done waiting for thread creations, error13 = %d\n", __FILE__, __LINE__, error13);
359
360    BPatch_Vector<BPatch_thread *> thrds;
361    proc->getThreads(thrds);
362    if (thrds.size() != NUM_THREADS)
363    {
364       logerror("[%s:%d] - Have %u threads, expected %u!\n",
365               __FILE__, __LINE__, thrds.size(), NUM_THREADS);
366       error13 = 1;
367    }
368
369    for (unsigned i=0; i<NUM_THREADS; i++)
370    {
371       if (!dyn_tids[i])
372       {
373          logerror("[%s:%d] - Thread %u was never created!\n",
374                  __FILE__, __LINE__, i);
375          missing_threads = true;
376       }
377    }
378    if(error13 || missing_threads) {
379       logerror("%s[%d]: ERROR during thread create stage, exiting\n", __FILE__, __LINE__);
380       logerror("*** Failed test_thread_6 (Threading Callbacks)\n");
381       if(proc && !proc->isTerminated())
382          proc->terminateExecution();
383       return FAILED;
384    }
385
386    upgrade_mutatee_state();
387    dprintf(stderr, "%s[%d]:  Now waiting for application to exit.\n", __FILE__, __LINE__);
388
389    while (!proc->isTerminated())
390       bpatch->waitForStatusChange();
391
392    num_attempts = 0;
393    while(deleted_threads != NUM_THREADS && num_attempts != TIMEOUT) {
394       num_attempts++;
395       P_sleep(1);
396    }
397
398    for (unsigned i=1; i<NUM_THREADS; i++)
399    {
400       if (!deleted_tids[i])
401       {
402          logerror("[%s:%d] - Thread %d wasn't deleted\n",
403                  __FILE__, __LINE__, i);
404          error13 = 1;
405       }
406    }
407
408    if (deleted_threads != NUM_THREADS || !deleted_tids[0])
409    {
410       logerror("[%s:%d] - %d threads deleted at termination." 
411            "  Expected %d\n", __FILE__, __LINE__, deleted_threads, NUM_THREADS);
412       error13 = 1;
413    }
414
415
416    if (error13)
417    {
418        logerror("*** Failed test_thread_6 (Threading Callbacks)\n");
419    } else {
420        logerror("Passed test_thread_6 (Threading Callbacks)\n");
421        logerror("Test completed without errors\n");
422        return PASSED;
423    }
424    return FAILED;
425 }
426
427 test_results_t test_thread_6_Mutator::executeTest() {
428    if (!bpatch->registerThreadEventCallback(BPatch_threadCreateEvent,
429                                             newthr) ||
430        !bpatch->registerThreadEventCallback(BPatch_threadDestroyEvent,
431                                             deadthr))
432    {
433       logerror("%s[%d]:  failed to register thread callback\n",
434               __FILE__, __LINE__);
435       return FAILED;
436    }
437
438    test_results_t rv = mutatorTest(bpatch);
439
440    if (!bpatch->removeThreadEventCallback(BPatch_threadCreateEvent, newthr) ||
441        !bpatch->removeThreadEventCallback(BPatch_threadDestroyEvent, deadthr))
442    {
443       logerror("%s[%d]:  failed to remove thread callback\n",
444               __FILE__, __LINE__);
445       return FAILED;
446    }
447
448    return rv;
449 }
450
451 test_results_t test_thread_6_Mutator::setup(ParameterDict &param) {
452    /* Grab info from param */
453    bpatch = (BPatch *)(param["bpatch"]->getPtr());
454    filename = param["pathname"]->getString();
455    logfilename = param["logfilename"]->getString();
456    
457    if ( param["useAttach"]->getInt() != 0 )
458    {
459       create_proc = false;
460    }
461    
462    return PASSED;
463 }