initial code repo
[stor4nfv.git] / src / ceph / src / test / system / systest_runnable.cc
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3 /*
4  * Ceph - scalable distributed file system
5  *
6  * Copyright (C) 2011 New Dream Network
7  *
8  * This is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License version 2.1, as published by the Free Software
11  * Foundation.  See file COPYING.
12  *
13  */
14
15 #include "include/compat.h"
16 #include "common/errno.h"
17 #include "systest_runnable.h"
18 #include "systest_settings.h"
19
20 #include <errno.h>
21 #include <pthread.h>
22 #include <sstream>
23 #include <stdint.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <string>
28 #include <sys/syscall.h>
29 #include <sys/types.h>
30 #include <sys/wait.h>
31 #include <unistd.h>
32 #include <vector>
33 #include <atomic>
34
35 using std::ostringstream;
36 using std::string;
37
38 static pid_t do_gettid(void)
39 {
40 #if defined(__linux__)
41   return static_cast < pid_t >(syscall(SYS_gettid));
42 #else
43   return static_cast < pid_t >(pthread_getthreadid_np());
44 #endif
45 }
46
47 std::atomic<unsigned> m_highest_id = { 0 };
48
49 SysTestRunnable::
50 SysTestRunnable(int argc, const char **argv)
51   : m_argc(0),
52     m_argv(NULL),
53     m_argv_orig(NULL)
54 {
55   m_started = false;
56   m_id = ++m_highest_id;
57   memset(&m_pthread, 0, sizeof(m_pthread));
58   update_id_str(false);
59   set_argv(argc, argv);
60 }
61
62 SysTestRunnable::
63 ~SysTestRunnable()
64 {
65   set_argv(0, NULL);
66 }
67
68 const char* SysTestRunnable::
69 get_id_str(void) const
70 {
71   return m_id_str;
72 }
73
74 int SysTestRunnable::
75 start()
76 {
77   if (m_started) {
78     return -EDOM;
79   }
80   int ret;
81   bool use_threads = SysTestSettings::inst().use_threads();
82   if (use_threads) {
83     ret = pthread_create(&m_pthread, NULL, systest_runnable_pthread_helper,
84                              static_cast<void*>(this));
85     if (ret)
86       return ret;
87     m_started = true;
88   } else {
89     std::string err_msg;
90     ret = preforker.prefork(err_msg);
91     if (ret < 0)
92       preforker.exit(ret);
93
94     if (preforker.is_child()) {
95       m_started = true;
96       void *retptr = systest_runnable_pthread_helper(static_cast<void*>(this));
97       preforker.exit((int)(uintptr_t)retptr);
98     } else {
99       m_started = true;
100     }
101   }
102   return 0;
103 }
104
105 std::string SysTestRunnable::
106 join()
107 {
108   if (!m_started) {
109     return "SysTestRunnable was never started.";
110   }
111   int ret;
112   bool use_threads = SysTestSettings::inst().use_threads();
113   if (use_threads) {
114     void *ptrretval;
115     ret = pthread_join(m_pthread, &ptrretval);
116     if (ret) {
117       ostringstream oss;
118       oss << "pthread_join failed with error " << ret;
119       return oss.str();
120     }
121     int retval = (int)(uintptr_t)ptrretval;
122     if (retval != 0) {
123       ostringstream oss;
124       oss << "ERROR " << retval;
125       return oss.str();
126     }
127     return "";
128   } else {
129     std::string err_msg;
130     ret = preforker.parent_wait(err_msg);
131     return err_msg;
132   }
133 }
134
135 std::string SysTestRunnable::
136 run_until_finished(std::vector < SysTestRunnable * > &runnables)
137 {
138   int index = 0;
139   for (std::vector < SysTestRunnable * >::const_iterator r = runnables.begin();
140       r != runnables.end(); ++r) {
141     int ret = (*r)->start();
142     if (ret) {
143       ostringstream oss;
144       oss << "run_until_finished: got error " << ret
145           << " when starting runnable " << index;
146       return oss.str();
147     }
148     ++index;
149   }
150
151   for (std::vector < SysTestRunnable * >::const_iterator r = runnables.begin();
152       r != runnables.end(); ++r) {
153     std::string rstr = (*r)->join();
154     if (!rstr.empty()) {
155       ostringstream oss;
156       oss << "run_until_finished: runnable " << (*r)->get_id_str() 
157           << ": got error: " << rstr;
158       return oss.str();
159     }
160   }
161   printf("*******************************\n");
162   return "";
163 }
164
165 void *systest_runnable_pthread_helper(void *arg)
166 {
167   SysTestRunnable *st = static_cast < SysTestRunnable * >(arg);
168   st->update_id_str(true);
169   printf("%s: starting.\n", st->get_id_str());
170   int ret = st->run();
171   printf("%s: shutting down.\n", st->get_id_str());
172   return (void*)(uintptr_t)ret;
173 }
174
175 void SysTestRunnable::
176 update_id_str(bool started)
177 {
178   bool use_threads = SysTestSettings::inst().use_threads();
179   char extra[128];
180   extra[0] = '\0';
181
182   if (started) {
183     if (use_threads)
184       snprintf(extra, sizeof(extra), "_[%d]", do_gettid());
185     else
186       snprintf(extra, sizeof(extra), "_[%d]", getpid());
187   }
188   if (use_threads)
189     snprintf(m_id_str, SysTestRunnable::ID_STR_SZ, "thread_%d%s", m_id, extra);
190   else
191     snprintf(m_id_str, SysTestRunnable::ID_STR_SZ, "process_%d%s", m_id, extra);
192 }
193
194 // Copy argv so that if some fiend decides to modify it, it's ok.
195 void SysTestRunnable::
196 set_argv(int argc, const char **argv)
197 {
198   if (m_argv_orig != NULL) {
199     for (int i = 0; i < m_argc; ++i)
200       free((void*)(m_argv_orig[i]));
201     delete[] m_argv_orig;
202     m_argv_orig = NULL;
203     delete[] m_argv;
204     m_argv = NULL;
205     m_argc = 0;
206   }
207   if (argv == NULL)
208     return;
209   m_argc = argc;
210   m_argv_orig = new const char*[m_argc+1];
211   for (int i = 0; i < m_argc; ++i)
212     m_argv_orig[i] = strdup(argv[i]);
213   m_argv_orig[argc] = NULL;
214   m_argv = new const char*[m_argc+1];
215   for (int i = 0; i <= m_argc; ++i)
216     m_argv[i] = m_argv_orig[i];
217 }