| OLD | NEW |
| (Empty) | |
| 1 import sys, unittest, subprocess, os.path, dis, types |
| 2 import dtrace |
| 3 from test.support import TESTFN, run_unittest, findfile |
| 4 |
| 5 sample = os.path.abspath(findfile("dtrace_sample.py")) |
| 6 if not dtrace.available : |
| 7 raise unittest.SkipTest("dtrace support not compiled in") |
| 8 |
| 9 def normalize(data) : |
| 10 # DTRACE keeps a per-CPU buffer, and when showing the fired probes, |
| 11 # buffers are concatenated. So if the operating system moves our |
| 12 # thread around, the straight result can be "non causal". |
| 13 # So we add timestamps to the probe firing, and sort by that field. |
| 14 try : |
| 15 result = data if isinstance(data, str) else data.decode("ascii") |
| 16 result = [i.split("\t") \ |
| 17 for i in result.replace("\r", "").split("\n") if len(i)] |
| 18 result.sort(key = lambda i: int(i[0])) |
| 19 result = "".join((i[1] for i in result)) |
| 20 result = result.replace(" ", "") |
| 21 except : |
| 22 # If something goes wrong, rebuild the value so we can see the |
| 23 # real result when the assert fails. |
| 24 result = data if isinstance(data, str) else data.decode("ascii") |
| 25 result = result.replace("\r", "").replace("\n", "") |
| 26 return result |
| 27 |
| 28 dscript = """ |
| 29 pid$target::PyEval_EvalCode:entry |
| 30 """ |
| 31 dscript = dscript.replace("\r", "").replace("\n", "") |
| 32 result, _ = subprocess.Popen(["dtrace", "-q", "-l", "-n", dscript, |
| 33 "-c", "%s %s" %(sys.executable, sample)], stdout=subprocess.PIPE, |
| 34 stderr=subprocess.STDOUT).communicate() |
| 35 if result.decode("ascii").split("\n")[1].split()[-2:] != \ |
| 36 ["PyEval_EvalCode", "entry"] : |
| 37 result2 = repr(result) |
| 38 raise unittest.SkipTest("dtrace seems not to be working. " + \ |
| 39 "Please, check your privileges. " + |
| 40 "Result: " +result2) |
| 41 |
| 42 class DTraceTestsNormal(unittest.TestCase) : |
| 43 def setUp(self) : |
| 44 self.optimize = False |
| 45 |
| 46 def test_function_entry_return(self) : |
| 47 dscript = """ |
| 48 python$target:::function-entry |
| 49 /(copyinstr(arg0)=="%(path)s") && |
| 50 (copyinstr(arg1)=="test_entry_return_and_stack")/ |
| 51 { |
| 52 self->trace = 1; |
| 53 } |
| 54 python$target:::function-entry,python$target:::function-return |
| 55 /(copyinstr(arg0)=="%(path)s") && (self->trace)/ |
| 56 { |
| 57 printf("%%d\t**%%s*%%s*%%s*%%d\\n", timestamp, |
| 58 probename, copyinstr(arg0), |
| 59 copyinstr(arg1), arg2); |
| 60 } |
| 61 python$target:::function-return |
| 62 /(copyinstr(arg0)=="%(path)s") && |
| 63 (copyinstr(arg1)=="test_entry_return_and_stack")/ |
| 64 { |
| 65 self->trace = 0; |
| 66 } |
| 67 """ %{"path":sample} |
| 68 |
| 69 dscript = dscript.replace("\r", "").replace("\n", "") |
| 70 expected_result = """ |
| 71 **function-entry*%(path)s*test_entry_return_and_stack*25 |
| 72 **function-entry*%(path)s*function_1*6 |
| 73 **function-return*%(path)s*function_1*7 |
| 74 **function-entry*%(path)s*function_2*10 |
| 75 **function-entry*%(path)s*function_1*6 |
| 76 **function-return*%(path)s*function_1*7 |
| 77 **function-return*%(path)s*function_2*11 |
| 78 **function-entry*%(path)s*function_3*14 |
| 79 **function-return*%(path)s*function_3*15 |
| 80 **function-entry*%(path)s*function_4*18 |
| 81 **function-return*%(path)s*function_4*19 |
| 82 **function-entry*%(path)s*function_5*22 |
| 83 **function-return*%(path)s*function_5*23 |
| 84 **function-return*%(path)s*test_entry_return_and_stack*30 |
| 85 """ %{"path":sample} |
| 86 |
| 87 command = "%s %s" %(sys.executable, sample) |
| 88 if self.optimize : |
| 89 command = "%s -OO %s" %(sys.executable, sample) |
| 90 actual_result, _ = subprocess.Popen(["dtrace", "-q", "-n", |
| 91 dscript, |
| 92 "-c", command], |
| 93 stdout=subprocess.PIPE, stderr=subprocess.STDOUT).communicate() |
| 94 |
| 95 actual_result = normalize(actual_result) |
| 96 expected_result = expected_result.replace("\r", "").replace("\n", |
| 97 "").replace(" ", "") |
| 98 self.assertEqual(actual_result, expected_result) |
| 99 |
| 100 @unittest.skipIf(sys.platform == 'darwin', |
| 101 "MacOS X doesn't support jstack()") |
| 102 def test_stack(self) : |
| 103 dscript = """ |
| 104 python$target:::function-entry |
| 105 /(copyinstr(arg0)=="%(path)s") && |
| 106 (copyinstr(arg1)=="test_entry_return_and_stack")/ |
| 107 { |
| 108 self->trace = 1; |
| 109 } |
| 110 python$target:::function-entry |
| 111 /(copyinstr(arg0)=="%(path)s") && (self->trace)/ |
| 112 { |
| 113 printf("[x]"); |
| 114 jstack(); |
| 115 } |
| 116 python$target:::function-return |
| 117 /(copyinstr(arg0)=="%(path)s") && |
| 118 (copyinstr(arg1)=="test_entry_return_and_stack")/ |
| 119 { |
| 120 self->trace = 0; |
| 121 } |
| 122 """ %{"path":sample} |
| 123 |
| 124 dscript = dscript.replace("\r", "").replace("\n", "") |
| 125 expected_result = """ |
| 126 [x] |
| 127 [%(path)s:25(test_entry_return_and_stack)] |
| 128 [x] |
| 129 [%(path)s:6(function_1)] |
| 130 [%(path)s:26(test_entry_return_and_stack)] |
| 131 [x] |
| 132 [%(path)s:10(function_2)] |
| 133 [%(path)s:27(test_entry_return_and_stack)] |
| 134 [x] |
| 135 [%(path)s:6(function_1)] |
| 136 [%(path)s:11(function_2)] |
| 137 [%(path)s:27(test_entry_return_and_stack)] |
| 138 [x] |
| 139 [%(path)s:14(function_3)] |
| 140 [%(path)s:28(test_entry_return_and_stack)] |
| 141 [x] |
| 142 [%(path)s:18(function_4)] |
| 143 [%(path)s:29(test_entry_return_and_stack)] |
| 144 [x] |
| 145 [%(path)s:22(function_5)] |
| 146 [%(path)s:30(test_entry_return_and_stack)] |
| 147 """ %{"path":sample} |
| 148 |
| 149 command = "%s %s" %(sys.executable, sample) |
| 150 if self.optimize : |
| 151 command = "%s -OO %s" %(sys.executable, sample) |
| 152 actual_result, _ = subprocess.Popen(["dtrace", "-q", "-n", |
| 153 dscript, |
| 154 "-c", command], |
| 155 stdout=subprocess.PIPE, stderr=subprocess.STDOUT).communicate() |
| 156 |
| 157 actual_result = [i for i in actual_result.decode("ascii").split("\n") \ |
| 158 if (("[" in i) and not i.endswith(" (<module>) ]"))] |
| 159 actual_result = "".join(actual_result) |
| 160 actual_result = actual_result.replace("\r", "").replace("\n", |
| 161 "").replace(" ", "") |
| 162 expected_result = expected_result.replace("\r", "").replace("\n", |
| 163 "").replace(" ", "") |
| 164 self.assertEqual(actual_result, expected_result) |
| 165 |
| 166 def test_garbage_collection(self) : |
| 167 dscript = """ |
| 168 python$target:::gc-start,python$target:::gc-done |
| 169 { |
| 170 printf("%d\t**%s(%ld)\\n", timestamp, probename, arg0); |
| 171 } |
| 172 """ |
| 173 |
| 174 dscript = dscript.replace("\r", "").replace("\n", "") |
| 175 command = "%s %s" %(sys.executable, sample) |
| 176 if self.optimize : |
| 177 command = "%s -OO %s" %(sys.executable, sample) |
| 178 actual_result, _ = subprocess.Popen(["dtrace", "-q", "-n", |
| 179 dscript, |
| 180 "-c", command], |
| 181 stdout=subprocess.PIPE, stderr=subprocess.STDOUT).communicate() |
| 182 |
| 183 actual_result = normalize(actual_result) |
| 184 for i in range(10) : |
| 185 actual_result = actual_result.replace(str(i), "") |
| 186 expected_result = "**gc-start()**gc-done()" * \ |
| 187 actual_result.count("**gc-start()**") |
| 188 |
| 189 self.assertEqual(actual_result, expected_result) |
| 190 |
| 191 def test_verify_opcodes(self) : |
| 192 # Verify that we are checking: |
| 193 opcodes = set(["CALL_FUNCTION", "CALL_FUNCTION_VAR", |
| 194 "CALL_FUNCTION_KW", "CALL_FUNCTION_VAR_KW"]) |
| 195 obj = compile(open(sample, encoding="utf-8").read(), "sample", "exec") |
| 196 class dump() : |
| 197 def __init__(self) : |
| 198 self.buf = [] |
| 199 def write(self, v) : |
| 200 self.buf.append(v) |
| 201 |
| 202 dump = dump() |
| 203 stdout = sys.stdout |
| 204 sys.stdout = dump |
| 205 for i in obj.co_consts : |
| 206 if isinstance(i, types.CodeType) and \ |
| 207 (i.co_name == 'test_entry_return_and_stack') : |
| 208 dis.dis(i) |
| 209 sys.stdout = stdout |
| 210 dump = "\n".join(dump.buf) |
| 211 dump = dump.replace("\r", "").replace("\n", "").split() |
| 212 for i in dump : |
| 213 opcodes.discard(i) |
| 214 # Are we verifying all the relevant opcodes? |
| 215 self.assertEqual(set(), opcodes) # Are we verifying all opcodes? |
| 216 |
| 217 def test_line(self) : |
| 218 dscript = """ |
| 219 python$target:::line |
| 220 /(copyinstr(arg0)=="%(path)s") && |
| 221 (copyinstr(arg1)=="test_line")/ |
| 222 { |
| 223 printf("%%d\t**%%s*%%s*%%s*%%d\\n", timestamp, |
| 224 probename, copyinstr(arg0), |
| 225 copyinstr(arg1), arg2); |
| 226 } |
| 227 """ %{"path":sample} |
| 228 |
| 229 dscript = dscript.replace("\r", "").replace("\n", "") |
| 230 expected_result = """ |
| 231 **line*%(path)s*test_line*33 |
| 232 **line*%(path)s*test_line*34 |
| 233 **line*%(path)s*test_line*35 |
| 234 **line*%(path)s*test_line*36 |
| 235 **line*%(path)s*test_line*37 |
| 236 **line*%(path)s*test_line*38 |
| 237 **line*%(path)s*test_line*34 |
| 238 **line*%(path)s*test_line*35 |
| 239 **line*%(path)s*test_line*36 |
| 240 **line*%(path)s*test_line*37 |
| 241 **line*%(path)s*test_line*38 |
| 242 **line*%(path)s*test_line*34 |
| 243 **line*%(path)s*test_line*39 |
| 244 """ %{"path":sample} |
| 245 |
| 246 command = "%s %s" %(sys.executable, sample) |
| 247 if self.optimize : |
| 248 command = "%s -OO %s" %(sys.executable, sample) |
| 249 actual_result, _ = subprocess.Popen(["dtrace", "-q", "-n", |
| 250 dscript, |
| 251 "-c", command], |
| 252 stdout=subprocess.PIPE, stderr=subprocess.STDOUT).communicate() |
| 253 |
| 254 actual_result = normalize(actual_result) |
| 255 expected_result = expected_result.replace("\r", "").replace("\n", |
| 256 "").replace(" ", "") |
| 257 self.assertEqual(actual_result, expected_result) |
| 258 |
| 259 def test_instance_creation_destruction(self) : |
| 260 dscript = """ |
| 261 python$target:::function-entry |
| 262 /(copyinstr(arg0)=="%(path)s") && |
| 263 (copyinstr(arg1)=="test_instance_creation_destruction")/ |
| 264 { |
| 265 self->trace = 1; |
| 266 } |
| 267 |
| 268 python$target:::instance-new-start, |
| 269 python$target:::instance-new-done, |
| 270 python$target:::instance-delete-start, |
| 271 python$target:::instance-delete-done |
| 272 /self->trace/ |
| 273 { |
| 274 printf("%%d\t**%%s* (%%s.%%s)\\n", timestamp, |
| 275 probename, copyinstr(arg1), copyinstr(arg0)); |
| 276 } |
| 277 |
| 278 python$target:::function-return |
| 279 /(copyinstr(arg0)=="%(path)s") && |
| 280 (copyinstr(arg1)=="test_instance_creation_destruction")/ |
| 281 { |
| 282 self->trace = 0; |
| 283 } |
| 284 """ %{"path":sample} |
| 285 |
| 286 dscript = dscript.replace("\r", "").replace("\n", "") |
| 287 expected_result = """ |
| 288 **instance-new-start*(__main__.old_style_class) |
| 289 **instance-new-done*(__main__.old_style_class) |
| 290 **instance-delete-start*(__main__.old_style_class) |
| 291 **instance-delete-done*(__main__.old_style_class) |
| 292 **instance-new-start*(__main__.new_style_class) |
| 293 **instance-new-done*(__main__.new_style_class) |
| 294 **instance-delete-start*(__main__.new_style_class) |
| 295 **instance-delete-done*(__main__.new_style_class) |
| 296 **instance-new-start*(__main__.old_style_class) |
| 297 **instance-new-done*(__main__.old_style_class) |
| 298 **instance-new-start*(__main__.new_style_class) |
| 299 **instance-new-done*(__main__.new_style_class) |
| 300 **instance-delete-start*(__main__.old_style_class) |
| 301 **instance-delete-done*(__main__.old_style_class) |
| 302 **instance-delete-start*(__main__.new_style_class) |
| 303 **instance-delete-done*(__main__.new_style_class) |
| 304 """ |
| 305 |
| 306 command = "%s %s" %(sys.executable, sample) |
| 307 if self.optimize : |
| 308 command = "%s -OO %s" %(sys.executable, sample) |
| 309 actual_result, _ = subprocess.Popen(["dtrace", "-q", "-n", |
| 310 dscript, |
| 311 "-c", command], |
| 312 stdout=subprocess.PIPE, stderr=subprocess.STDOUT).communicate() |
| 313 |
| 314 actual_result = normalize(actual_result) |
| 315 expected_result = expected_result.replace("\r", "").replace("\n", |
| 316 "").replace(" ", "") |
| 317 self.assertEqual(actual_result, expected_result) |
| 318 |
| 319 def test_unicode_function_entry_return(self) : |
| 320 dscript = """ |
| 321 python$target:::function-entry |
| 322 /(copyinstr(arg0)=="%(path)s") && |
| 323 (copyinstr(arg1)=="test_unicode_entry_return_and_stack")/ |
| 324 { |
| 325 self->trace = 1; |
| 326 } |
| 327 python$target:::function-entry,python$target:::function-return |
| 328 /(copyinstr(arg0)=="%(path)s") && (self->trace)/ |
| 329 { |
| 330 printf("%%d\t**%%s*%%s*%%s*%%d\\n", timestamp, |
| 331 probename, copyinstr(arg0), |
| 332 copyinstr(arg1), arg2); |
| 333 } |
| 334 python$target:::function-return |
| 335 /(copyinstr(arg0)=="%(path)s") && |
| 336 (copyinstr(arg1)=="test_unicode_entry_return_and_stack")/ |
| 337 { |
| 338 self->trace = 0; |
| 339 } |
| 340 """ %{"path":sample} |
| 341 |
| 342 dscript = dscript.replace("\r", "").replace("\n", "") |
| 343 expected_result = """ |
| 344 **function-entry*%(path)s*test_unicode_entry_return_and_stack*41 |
| 345 **function-entry*%(path)s*únícódé*42 |
| 346 **function-return*%(path)s*únícódé*43 |
| 347 **function-return*%(path)s*test_unicode_entry_return_and_stack*44 |
| 348 """ %{"path":sample} |
| 349 |
| 350 command = "%s %s" %(sys.executable, sample) |
| 351 if self.optimize : |
| 352 command = "%s -OO %s" %(sys.executable, sample) |
| 353 actual_result, _ = subprocess.Popen(["dtrace", "-q", "-n", |
| 354 dscript, |
| 355 "-c", command], |
| 356 stdout=subprocess.PIPE, stderr=subprocess.STDOUT).communicate() |
| 357 |
| 358 actual_result = actual_result.decode("utf8") |
| 359 actual_result = normalize(actual_result) |
| 360 expected_result = expected_result.replace("\r", "").replace("\n", |
| 361 "").replace(" ", "") |
| 362 self.assertEqual(actual_result, expected_result) |
| 363 |
| 364 @unittest.skipIf(sys.platform == 'darwin', |
| 365 "MacOS X doesn't support jstack()") |
| 366 def test_unicode_stack(self) : |
| 367 dscript = """ |
| 368 python$target:::function-entry |
| 369 /(copyinstr(arg0)=="%(path)s") && |
| 370 (copyinstr(arg1)=="test_unicode_entry_return_and_stack")/ |
| 371 { |
| 372 self->trace = 1; |
| 373 } |
| 374 python$target:::function-entry |
| 375 /(copyinstr(arg0)=="%(path)s") && (self->trace)/ |
| 376 { |
| 377 printf("[x]"); |
| 378 jstack(); |
| 379 } |
| 380 python$target:::function-return |
| 381 /(copyinstr(arg0)=="%(path)s") && |
| 382 (copyinstr(arg1)=="test_unicode_entry_return_and_stack")/ |
| 383 { |
| 384 self->trace = 0; |
| 385 } |
| 386 """ %{"path":sample} |
| 387 |
| 388 dscript = dscript.replace("\r", "").replace("\n", "") |
| 389 expected_result = """ |
| 390 [x] |
| 391 [%(path)s:41(test_unicode_entry_return_and_stack)] |
| 392 [x] |
| 393 [%(path)s:42(únícódé)] |
| 394 [%(path)s:44(test_unicode_entry_return_and_stack)] |
| 395 """ %{"path":sample} |
| 396 |
| 397 command = "%s %s" %(sys.executable, sample) |
| 398 if self.optimize : |
| 399 command = "%s -OO %s" %(sys.executable, sample) |
| 400 actual_result, _ = subprocess.Popen(["dtrace", "-q", "-n", |
| 401 dscript, |
| 402 "-c", command], |
| 403 stdout=subprocess.PIPE, stderr=subprocess.STDOUT).communicate() |
| 404 |
| 405 actual_result = [i for i in actual_result.decode("utf8").split("\n") \ |
| 406 if (("[" in i) and not i.endswith(" (<module>) ]"))] |
| 407 actual_result = "".join(actual_result) |
| 408 actual_result = actual_result.replace("\r", "").replace("\n", |
| 409 "").replace(" ", "") |
| 410 expected_result = expected_result.replace("\r", "").replace("\n", |
| 411 "").replace(" ", "") |
| 412 self.assertEqual(actual_result, expected_result) |
| 413 |
| 414 |
| 415 |
| 416 # This class try to verify that dtrace probes |
| 417 # are still working with optimizations enabled in the bytecode. |
| 418 # |
| 419 # Some tests will not actually verify it. For instance, |
| 420 # source code compilation follows optimization status of |
| 421 # current working Python. So, you should run the test |
| 422 # both with an optimizing and a non optimizing Python. |
| 423 class DTraceTestsOptimize(DTraceTestsNormal) : |
| 424 def setUp(self) : |
| 425 self.optimize = True |
| 426 |
| 427 |
| 428 def test_main(): |
| 429 run_unittest(DTraceTestsNormal) |
| 430 run_unittest(DTraceTestsOptimize) |
| 431 |
| 432 if __name__ == '__main__': |
| 433 test_main() |
| 434 |
| OLD | NEW |