| LEFT | RIGHT |
| 1 import sys, unittest, subprocess, os.path, dis, types | 1 import sys, unittest, subprocess, os.path, dis, types |
| 2 import dtrace | 2 import dtrace |
| 3 from test.support import TESTFN, run_unittest, findfile | 3 from test.support import TESTFN, run_unittest, findfile |
| 4 | 4 |
| 5 sample = os.path.abspath(findfile("dtrace_sample.py")) | 5 sample = os.path.abspath(findfile("dtrace_sample.py")) |
| 6 if not dtrace.available : | 6 if not dtrace.available : |
| 7 raise unittest.SkipTest("dtrace support not compiled in") | 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 |
| 8 | 27 |
| 9 dscript = """ | 28 dscript = """ |
| 10 pid$target::PyEval_EvalCode:entry | 29 pid$target::PyEval_EvalCode:entry |
| 11 """ | 30 """ |
| 12 dscript = dscript.replace("\r", "").replace("\n", "") | 31 dscript = dscript.replace("\r", "").replace("\n", "") |
| 13 result, _ = subprocess.Popen(["dtrace", "-q", "-l", "-n", dscript, | 32 result, _ = subprocess.Popen(["dtrace", "-q", "-l", "-n", dscript, |
| 14 "-c", "%s %s" %(sys.executable, sample)], stdout=subprocess.PIPE, | 33 "-c", "%s %s" %(sys.executable, sample)], stdout=subprocess.PIPE, |
| 15 stderr=subprocess.STDOUT).communicate() | 34 stderr=subprocess.STDOUT).communicate() |
| 16 if result.decode("ascii").split("\n")[1].split()[-2:] != \ | 35 if result.decode("ascii").split("\n")[1].split()[-2:] != \ |
| 17 ["PyEval_EvalCode", "entry"] : | 36 ["PyEval_EvalCode", "entry"] : |
| (...skipping 10 matching lines...) Expand all Loading... |
| 28 dscript = """ | 47 dscript = """ |
| 29 python$target:::function-entry | 48 python$target:::function-entry |
| 30 /(copyinstr(arg0)=="%(path)s") && | 49 /(copyinstr(arg0)=="%(path)s") && |
| 31 (copyinstr(arg1)=="test_entry_return_and_stack")/ | 50 (copyinstr(arg1)=="test_entry_return_and_stack")/ |
| 32 { | 51 { |
| 33 self->trace = 1; | 52 self->trace = 1; |
| 34 } | 53 } |
| 35 python$target:::function-entry,python$target:::function-return | 54 python$target:::function-entry,python$target:::function-return |
| 36 /(copyinstr(arg0)=="%(path)s") && (self->trace)/ | 55 /(copyinstr(arg0)=="%(path)s") && (self->trace)/ |
| 37 { | 56 { |
| 38 printf("**%%s*%%s*%%s*%%d\\n", probename, copyinstr(arg0), | 57 printf("%%d\t**%%s*%%s*%%s*%%d\\n", timestamp, |
| 58 probename, copyinstr(arg0), |
| 39 copyinstr(arg1), arg2); | 59 copyinstr(arg1), arg2); |
| 40 } | 60 } |
| 41 python$target:::function-return | 61 python$target:::function-return |
| 42 /(copyinstr(arg0)=="%(path)s") && | 62 /(copyinstr(arg0)=="%(path)s") && |
| 43 (copyinstr(arg1)=="test_entry_return_and_stack")/ | 63 (copyinstr(arg1)=="test_entry_return_and_stack")/ |
| 44 { | 64 { |
| 45 self->trace = 0; | 65 self->trace = 0; |
| 46 } | 66 } |
| 47 """ %{"path":sample} | 67 """ %{"path":sample} |
| 48 | 68 |
| (...skipping 16 matching lines...) Expand all Loading... |
| 65 """ %{"path":sample} | 85 """ %{"path":sample} |
| 66 | 86 |
| 67 command = "%s %s" %(sys.executable, sample) | 87 command = "%s %s" %(sys.executable, sample) |
| 68 if self.optimize : | 88 if self.optimize : |
| 69 command = "%s -OO %s" %(sys.executable, sample) | 89 command = "%s -OO %s" %(sys.executable, sample) |
| 70 actual_result, _ = subprocess.Popen(["dtrace", "-q", "-n", | 90 actual_result, _ = subprocess.Popen(["dtrace", "-q", "-n", |
| 71 dscript, | 91 dscript, |
| 72 "-c", command], | 92 "-c", command], |
| 73 stdout=subprocess.PIPE, stderr=subprocess.STDOUT).communicate() | 93 stdout=subprocess.PIPE, stderr=subprocess.STDOUT).communicate() |
| 74 | 94 |
| 75 actual_result = actual_result.decode("ascii").replace("\r", | 95 actual_result = normalize(actual_result) |
| 76 "").replace("\n", "").replace(" ", "") | |
| 77 expected_result = expected_result.replace("\r", "").replace("\n", | 96 expected_result = expected_result.replace("\r", "").replace("\n", |
| 78 "").replace(" ", "") | 97 "").replace(" ", "") |
| 79 self.assertEqual(actual_result, expected_result) | 98 self.assertEqual(actual_result, expected_result) |
| 80 | 99 |
| 81 @unittest.skipIf(sys.platform == 'darwin', | 100 @unittest.skipIf(sys.platform == 'darwin', |
| 82 "MacOS X doesn't support jstack()") | 101 "MacOS X doesn't support jstack()") |
| 83 def test_stack(self) : | 102 def test_stack(self) : |
| 84 dscript = """ | 103 dscript = """ |
| 85 python$target:::function-entry | 104 python$target:::function-entry |
| 86 /(copyinstr(arg0)=="%(path)s") && | 105 /(copyinstr(arg0)=="%(path)s") && |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 141 actual_result = actual_result.replace("\r", "").replace("\n", | 160 actual_result = actual_result.replace("\r", "").replace("\n", |
| 142 "").replace(" ", "") | 161 "").replace(" ", "") |
| 143 expected_result = expected_result.replace("\r", "").replace("\n", | 162 expected_result = expected_result.replace("\r", "").replace("\n", |
| 144 "").replace(" ", "") | 163 "").replace(" ", "") |
| 145 self.assertEqual(actual_result, expected_result) | 164 self.assertEqual(actual_result, expected_result) |
| 146 | 165 |
| 147 def test_garbage_collection(self) : | 166 def test_garbage_collection(self) : |
| 148 dscript = """ | 167 dscript = """ |
| 149 python$target:::gc-start,python$target:::gc-done | 168 python$target:::gc-start,python$target:::gc-done |
| 150 { | 169 { |
| 151 printf("**%s(%ld)\\n", probename, arg0); | 170 printf("%d\t**%s(%ld)\\n", timestamp, probename, arg0); |
| 152 } | 171 } |
| 153 """ | 172 """ |
| 154 | 173 |
| 155 dscript = dscript.replace("\r", "").replace("\n", "") | 174 dscript = dscript.replace("\r", "").replace("\n", "") |
| 156 command = "%s %s" %(sys.executable, sample) | 175 command = "%s %s" %(sys.executable, sample) |
| 157 if self.optimize : | 176 if self.optimize : |
| 158 command = "%s -OO %s" %(sys.executable, sample) | 177 command = "%s -OO %s" %(sys.executable, sample) |
| 159 actual_result, _ = subprocess.Popen(["dtrace", "-q", "-n", | 178 actual_result, _ = subprocess.Popen(["dtrace", "-q", "-n", |
| 160 dscript, | 179 dscript, |
| 161 "-c", command], | 180 "-c", command], |
| 162 stdout=subprocess.PIPE, stderr=subprocess.STDOUT).communicate() | 181 stdout=subprocess.PIPE, stderr=subprocess.STDOUT).communicate() |
| 163 | 182 |
| 164 actual_result = "".join(actual_result.decode("ascii")) | 183 actual_result = normalize(actual_result) |
| 165 actual_result = actual_result.replace("\r", "").replace("\n", | |
| 166 "").replace(" ", "") | |
| 167 for i in range(10) : | 184 for i in range(10) : |
| 168 actual_result = actual_result.replace(str(i), "") | 185 actual_result = actual_result.replace(str(i), "") |
| 169 expected_result = "**gc-start()**gc-done()" * \ | 186 expected_result = "**gc-start()**gc-done()" * \ |
| 170 actual_result.count("**gc-start()**") | 187 actual_result.count("**gc-start()**") |
| 171 | 188 |
| 172 self.assertEqual(actual_result, expected_result) | 189 self.assertEqual(actual_result, expected_result) |
| 173 | 190 |
| 174 def test_verify_opcodes(self) : | 191 def test_verify_opcodes(self) : |
| 175 # Verify that we are checking: | 192 # Verify that we are checking: |
| 176 opcodes = set(["CALL_FUNCTION", "CALL_FUNCTION_VAR", | 193 opcodes = set(["CALL_FUNCTION", "CALL_FUNCTION_VAR", |
| (...skipping 19 matching lines...) Expand all Loading... |
| 196 opcodes.discard(i) | 213 opcodes.discard(i) |
| 197 # Are we verifying all the relevant opcodes? | 214 # Are we verifying all the relevant opcodes? |
| 198 self.assertEqual(set(), opcodes) # Are we verifying all opcodes? | 215 self.assertEqual(set(), opcodes) # Are we verifying all opcodes? |
| 199 | 216 |
| 200 def test_line(self) : | 217 def test_line(self) : |
| 201 dscript = """ | 218 dscript = """ |
| 202 python$target:::line | 219 python$target:::line |
| 203 /(copyinstr(arg0)=="%(path)s") && | 220 /(copyinstr(arg0)=="%(path)s") && |
| 204 (copyinstr(arg1)=="test_line")/ | 221 (copyinstr(arg1)=="test_line")/ |
| 205 { | 222 { |
| 206 printf("**%%s*%%s*%%s*%%d\\n", probename, copyinstr(arg0), | 223 printf("%%d\t**%%s*%%s*%%s*%%d\\n", timestamp, |
| 224 probename, copyinstr(arg0), |
| 207 copyinstr(arg1), arg2); | 225 copyinstr(arg1), arg2); |
| 208 } | 226 } |
| 209 """ %{"path":sample} | 227 """ %{"path":sample} |
| 210 | 228 |
| 211 dscript = dscript.replace("\r", "").replace("\n", "") | 229 dscript = dscript.replace("\r", "").replace("\n", "") |
| 212 expected_result = """ | 230 expected_result = """ |
| 213 **line*%(path)s*test_line*33 | 231 **line*%(path)s*test_line*33 |
| 214 **line*%(path)s*test_line*34 | 232 **line*%(path)s*test_line*34 |
| 215 **line*%(path)s*test_line*35 | 233 **line*%(path)s*test_line*35 |
| 216 **line*%(path)s*test_line*36 | 234 **line*%(path)s*test_line*36 |
| 217 **line*%(path)s*test_line*37 | 235 **line*%(path)s*test_line*37 |
| 218 **line*%(path)s*test_line*38 | 236 **line*%(path)s*test_line*38 |
| 219 **line*%(path)s*test_line*34 | 237 **line*%(path)s*test_line*34 |
| 220 **line*%(path)s*test_line*35 | 238 **line*%(path)s*test_line*35 |
| 221 **line*%(path)s*test_line*36 | 239 **line*%(path)s*test_line*36 |
| 222 **line*%(path)s*test_line*37 | 240 **line*%(path)s*test_line*37 |
| 223 **line*%(path)s*test_line*38 | 241 **line*%(path)s*test_line*38 |
| 224 **line*%(path)s*test_line*34 | 242 **line*%(path)s*test_line*34 |
| 225 **line*%(path)s*test_line*39 | 243 **line*%(path)s*test_line*39 |
| 226 """ %{"path":sample} | 244 """ %{"path":sample} |
| 227 | 245 |
| 228 command = "%s %s" %(sys.executable, sample) | 246 command = "%s %s" %(sys.executable, sample) |
| 229 if self.optimize : | 247 if self.optimize : |
| 230 command = "%s -OO %s" %(sys.executable, sample) | 248 command = "%s -OO %s" %(sys.executable, sample) |
| 231 actual_result, _ = subprocess.Popen(["dtrace", "-q", "-n", | 249 actual_result, _ = subprocess.Popen(["dtrace", "-q", "-n", |
| 232 dscript, | 250 dscript, |
| 233 "-c", command], | 251 "-c", command], |
| 234 stdout=subprocess.PIPE, stderr=subprocess.STDOUT).communicate() | 252 stdout=subprocess.PIPE, stderr=subprocess.STDOUT).communicate() |
| 235 | 253 |
| 236 actual_result = actual_result.decode("ascii").replace("\r", | 254 actual_result = normalize(actual_result) |
| 237 "").replace("\n", "").replace(" ", "") | |
| 238 expected_result = expected_result.replace("\r", "").replace("\n", | 255 expected_result = expected_result.replace("\r", "").replace("\n", |
| 239 "").replace(" ", "") | 256 "").replace(" ", "") |
| 240 self.assertEqual(actual_result, expected_result) | 257 self.assertEqual(actual_result, expected_result) |
| 241 | 258 |
| 242 def test_instance_creation_destruction(self) : | 259 def test_instance_creation_destruction(self) : |
| 243 dscript = """ | 260 dscript = """ |
| 244 python$target:::function-entry | 261 python$target:::function-entry |
| 245 /(copyinstr(arg0)=="%(path)s") && | 262 /(copyinstr(arg0)=="%(path)s") && |
| 246 (copyinstr(arg1)=="test_instance_creation_destruction")/ | 263 (copyinstr(arg1)=="test_instance_creation_destruction")/ |
| 247 { | 264 { |
| 248 self->trace = 1; | 265 self->trace = 1; |
| 249 } | 266 } |
| 250 | 267 |
| 251 python$target:::instance-new-start, | 268 python$target:::instance-new-start, |
| 252 python$target:::instance-new-done, | 269 python$target:::instance-new-done, |
| 253 python$target:::instance-delete-start, | 270 python$target:::instance-delete-start, |
| 254 python$target:::instance-delete-done | 271 python$target:::instance-delete-done |
| 255 /self->trace/ | 272 /self->trace/ |
| 256 { | 273 { |
| 257 printf("**%%s* (%%s.%%s)\\n", probename, copyinstr(arg1), copyinstr(arg0)); | 274 printf("%%d\t**%%s* (%%s.%%s)\\n", timestamp, |
| 275 probename, copyinstr(arg1), copyinstr(arg0)); |
| 258 } | 276 } |
| 259 | 277 |
| 260 python$target:::function-return | 278 python$target:::function-return |
| 261 /(copyinstr(arg0)=="%(path)s") && | 279 /(copyinstr(arg0)=="%(path)s") && |
| 262 (copyinstr(arg1)=="test_instance_creation_destruction")/ | 280 (copyinstr(arg1)=="test_instance_creation_destruction")/ |
| 263 { | 281 { |
| 264 self->trace = 0; | 282 self->trace = 0; |
| 265 } | 283 } |
| 266 """ %{"path":sample} | 284 """ %{"path":sample} |
| 267 | 285 |
| (...skipping 18 matching lines...) Expand all Loading... |
| 286 """ | 304 """ |
| 287 | 305 |
| 288 command = "%s %s" %(sys.executable, sample) | 306 command = "%s %s" %(sys.executable, sample) |
| 289 if self.optimize : | 307 if self.optimize : |
| 290 command = "%s -OO %s" %(sys.executable, sample) | 308 command = "%s -OO %s" %(sys.executable, sample) |
| 291 actual_result, _ = subprocess.Popen(["dtrace", "-q", "-n", | 309 actual_result, _ = subprocess.Popen(["dtrace", "-q", "-n", |
| 292 dscript, | 310 dscript, |
| 293 "-c", command], | 311 "-c", command], |
| 294 stdout=subprocess.PIPE, stderr=subprocess.STDOUT).communicate() | 312 stdout=subprocess.PIPE, stderr=subprocess.STDOUT).communicate() |
| 295 | 313 |
| 296 actual_result = actual_result.decode("ascii").replace("\r", | 314 actual_result = normalize(actual_result) |
| 297 "").replace("\n", "").replace(" ", "") | |
| 298 expected_result = expected_result.replace("\r", "").replace("\n", | 315 expected_result = expected_result.replace("\r", "").replace("\n", |
| 299 "").replace(" ", "") | 316 "").replace(" ", "") |
| 300 self.assertEqual(actual_result, expected_result) | 317 self.assertEqual(actual_result, expected_result) |
| 301 | 318 |
| 302 def test_unicode_function_entry_return(self) : | 319 def test_unicode_function_entry_return(self) : |
| 303 dscript = """ | 320 dscript = """ |
| 304 python$target:::function-entry | 321 python$target:::function-entry |
| 305 /(copyinstr(arg0)=="%(path)s") && | 322 /(copyinstr(arg0)=="%(path)s") && |
| 306 (copyinstr(arg1)=="test_unicode_entry_return_and_stack")/ | 323 (copyinstr(arg1)=="test_unicode_entry_return_and_stack")/ |
| 307 { | 324 { |
| 308 self->trace = 1; | 325 self->trace = 1; |
| 309 } | 326 } |
| 310 python$target:::function-entry,python$target:::function-return | 327 python$target:::function-entry,python$target:::function-return |
| 311 /(copyinstr(arg0)=="%(path)s") && (self->trace)/ | 328 /(copyinstr(arg0)=="%(path)s") && (self->trace)/ |
| 312 { | 329 { |
| 313 printf("**%%s*%%s*%%s*%%d\\n", probename, copyinstr(arg0), | 330 printf("%%d\t**%%s*%%s*%%s*%%d\\n", timestamp, |
| 331 probename, copyinstr(arg0), |
| 314 copyinstr(arg1), arg2); | 332 copyinstr(arg1), arg2); |
| 315 } | 333 } |
| 316 python$target:::function-return | 334 python$target:::function-return |
| 317 /(copyinstr(arg0)=="%(path)s") && | 335 /(copyinstr(arg0)=="%(path)s") && |
| 318 (copyinstr(arg1)=="test_unicode_entry_return_and_stack")/ | 336 (copyinstr(arg1)=="test_unicode_entry_return_and_stack")/ |
| 319 { | 337 { |
| 320 self->trace = 0; | 338 self->trace = 0; |
| 321 } | 339 } |
| 322 """ %{"path":sample} | 340 """ %{"path":sample} |
| 323 | 341 |
| 324 dscript = dscript.replace("\r", "").replace("\n", "") | 342 dscript = dscript.replace("\r", "").replace("\n", "") |
| 325 expected_result = """ | 343 expected_result = """ |
| 326 **function-entry*%(path)s*test_unicode_entry_return_and_stack*41 | 344 **function-entry*%(path)s*test_unicode_entry_return_and_stack*41 |
| 327 **function-entry*%(path)s*únícódé*42 | 345 **function-entry*%(path)s*únícódé*42 |
| 328 **function-return*%(path)s*únícódé*43 | 346 **function-return*%(path)s*únícódé*43 |
| 329 **function-return*%(path)s*test_unicode_entry_return_and_stack*44 | 347 **function-return*%(path)s*test_unicode_entry_return_and_stack*44 |
| 330 """ %{"path":sample} | 348 """ %{"path":sample} |
| 331 | 349 |
| 332 command = "%s %s" %(sys.executable, sample) | 350 command = "%s %s" %(sys.executable, sample) |
| 333 if self.optimize : | 351 if self.optimize : |
| 334 command = "%s -OO %s" %(sys.executable, sample) | 352 command = "%s -OO %s" %(sys.executable, sample) |
| 335 actual_result, _ = subprocess.Popen(["dtrace", "-q", "-n", | 353 actual_result, _ = subprocess.Popen(["dtrace", "-q", "-n", |
| 336 dscript, | 354 dscript, |
| 337 "-c", command], | 355 "-c", command], |
| 338 stdout=subprocess.PIPE, stderr=subprocess.STDOUT).communicate() | 356 stdout=subprocess.PIPE, stderr=subprocess.STDOUT).communicate() |
| 339 | 357 |
| 340 actual_result = actual_result.decode("utf8").replace("\r", | 358 actual_result = actual_result.decode("utf8") |
| 341 "").replace("\n", "").replace(" ", "") | 359 actual_result = normalize(actual_result) |
| 342 expected_result = expected_result.replace("\r", "").replace("\n", | 360 expected_result = expected_result.replace("\r", "").replace("\n", |
| 343 "").replace(" ", "") | 361 "").replace(" ", "") |
| 344 self.assertEqual(actual_result, expected_result) | 362 self.assertEqual(actual_result, expected_result) |
| 345 | 363 |
| 364 @unittest.skipIf(sys.platform == 'darwin', |
| 365 "MacOS X doesn't support jstack()") |
| 346 def test_unicode_stack(self) : | 366 def test_unicode_stack(self) : |
| 347 dscript = """ | 367 dscript = """ |
| 348 python$target:::function-entry | 368 python$target:::function-entry |
| 349 /(copyinstr(arg0)=="%(path)s") && | 369 /(copyinstr(arg0)=="%(path)s") && |
| 350 (copyinstr(arg1)=="test_unicode_entry_return_and_stack")/ | 370 (copyinstr(arg1)=="test_unicode_entry_return_and_stack")/ |
| 351 { | 371 { |
| 352 self->trace = 1; | 372 self->trace = 1; |
| 353 } | 373 } |
| 354 python$target:::function-entry | 374 python$target:::function-entry |
| 355 /(copyinstr(arg0)=="%(path)s") && (self->trace)/ | 375 /(copyinstr(arg0)=="%(path)s") && (self->trace)/ |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 405 self.optimize = True | 425 self.optimize = True |
| 406 | 426 |
| 407 | 427 |
| 408 def test_main(): | 428 def test_main(): |
| 409 run_unittest(DTraceTestsNormal) | 429 run_unittest(DTraceTestsNormal) |
| 410 run_unittest(DTraceTestsOptimize) | 430 run_unittest(DTraceTestsOptimize) |
| 411 | 431 |
| 412 if __name__ == '__main__': | 432 if __name__ == '__main__': |
| 413 test_main() | 433 test_main() |
| 414 | 434 |
| LEFT | RIGHT |