Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

714 lines
21 KiB

  1. @rem = '
  2. @goto endofperl
  3. ';
  4. $USAGE = "
  5. Usage: $0 files
  6. Parameters are set via enviroment vars.
  7. LS_COG Change Order Guid of the form xxxxxxxx
  8. LS_FID File ID of the form xxxxxxxx xxxxxxxx
  9. LS_PFID Parent File ID of the form xxxxxxxx xxxxxxxx
  10. LS_FGUID File Guid of the form xxxxxxxx
  11. LS_PFGUID File parent Guid of the form xxxxxxxx
  12. LS_FNAME Filename
  13. LS_FRSVSN Vol Sequence number of the form xxxxxxxx xxxxxxxx
  14. LS_CXTG Connection Guid of the form xxxxxxxx
  15. LS_REPLCXTION Text string of replica set names to print :S: and :X: records
  16. LS_TRACETAG Text string starting after the leading [ at the end of the :: records.
  17. LS_BUGNAME Test string to put at the top of the output file.
  18. A null env var means the log is not filtered on that parameter.
  19. Each env var can have multiple values separated by a comma. These params
  20. are used as search patterns and the , is replaced by a |
  21. Watch out for left over trailing commas. They will foul up the search.
  22. e.g. if you wanted to extract data related to two change orders you would set
  23. set LS_COG==a2d42bf8,90506912
  24. where these are the first dwords of the stringized change order guid.
  25. You can also filter IDTABLE records based on the Flags field.
  26. You need to edit the script for that.
  27. ";
  28. die $USAGE unless @ARGV;
  29. $total_cos = 0;
  30. $InFile = "";
  31. $reconcilex = 0;
  32. $getlocnx = 0;
  33. $costart = 0;
  34. $idstart = 0;
  35. $bx = 0;
  36. $bmax = 63;
  37. $IDREC_FLAGS_DELETED = 0x00000001;
  38. $IDREC_FLAGS_CREATE_DEFERRED = 0x00000002;
  39. $IDREC_FLAGS_DELETE_DEFERRED = 0x00000004;
  40. $IDREC_FLAGS_RENAME_DEFERRED = 0x00000008;
  41. $IDREC_FLAGS_NEW_FILE_IN_PROGRESS = 0x00000010;
  42. $IDREC_FLAGS_ENUM_PENDING = 0x00000020;
  43. #
  44. # Search params (except for idflagsset/clear) are set with the
  45. # environment variables below. Each can take a comma delimited
  46. # list. All guid params take only the first DWORD of the guid string.
  47. #
  48. printf("LS_BUGNAME: %s\n", $ENV{'LS_BUGNAME'});
  49. $cog = $ENV{'LS_COG'}; printf("LS_COG: %s\n", $cog);
  50. $fid = $ENV{'LS_FID'}; printf("LS_FID: %s\n", $fid);
  51. $pfid = $ENV{'LS_PFID'}; printf("LS_PFID: %s\n", $pfid);
  52. $fguid = $ENV{'LS_FGUID'}; printf("LS_FGUID: %s\n", $fguid);
  53. $pfguid = $ENV{'LS_PFGUID'}; printf("LS_PFGUID: %s\n", $pfguid);
  54. $fname = $ENV{'LS_FNAME'}; printf("LS_FNAME: %s\n", $fname);
  55. $tracetag = $ENV{'LS_TRACETAG'}; printf("LS_TRACETAG: %s\n", $tracetag);
  56. $frsvsn = $ENV{'LS_FRSVSN'}; printf("LS_FRSVSN: %s\n", $frsvsn);
  57. $cxtg = $ENV{'LS_CXTG'}; printf("LS_CXTG: %s\n", $cxtg);
  58. $replcxtion= $ENV{'LS_REPLCXTION'}; printf("LS_REPLCXTION: %s\n", $replcxtion);
  59. #
  60. # replace commas with | for pattern matching.
  61. #
  62. if ($fname ne "") { $fname = "($fname)"; $fname =~ s/,/\|/g; }
  63. if ($fid ne "") { $fid = "($fid)"; $fid =~ s/,/\|/g; }
  64. if ($pfid ne "") { $pfid = "($pfid)"; $pfid =~ s/,/\|/g; }
  65. if ($fguid ne "") { $fguid = "($fguid)"; $fguid =~ s/,/\|/g; }
  66. if ($pfguid ne "") { $pfguid = "($pfguid)"; $pfguid =~ s/,/\|/g; }
  67. if ($cog ne "") { $cog = "($cog)"; $cog =~ s/,/\|/g; }
  68. if ($tracetag ne "") { $tracetag = "($tracetag)"; $tracetag =~ s/,/\|/g; }
  69. if ($frsvsn ne "") { $frsvsn = "($frsvsn)"; $frsvsn =~ s/,/\|/g; }
  70. if ($cxtg ne "") { $cxtg = "($cxtg)"; $cxtg =~ s/,/\|/g; }
  71. if ($replcxtion ne ""){ $replcxtion = "($replcxtion)"; $replcxtion =~ s/,/\|/g;}
  72. #
  73. # IDTable records will be printed only if:
  74. # the bits in idflagsset are set in the IDTable Flags field AND
  75. # the bits in idflagsclear are clear in the IDTable Flags field.
  76. # e.g. if idflagsclear is set to IDREC_FLAGS_DELETED and
  77. # idflagsset is set to zero then deleted IDTable entrys are not printed.
  78. #
  79. # #### $idflagsclear = $IDREC_FLAGS_DELETED;
  80. $idflagsclear = 0;
  81. $idflagsset = 0;
  82. printf("\n\n");
  83. print $0 @argv;
  84. printf("fname: %s\n", $fname) if ($fname ne "");
  85. printf("fid: %s\n", $fid) if ($fid ne "");
  86. printf("pfid: %s\n", $pfid) if ($pfid ne "");
  87. printf("fguid: %s\n", $fguid) if ($fguid ne "");
  88. printf("pfguid: %s\n", $pfguid) if ($pfguid ne "");
  89. printf("cog: %s\n", $cog) if ($cog ne "");
  90. printf("tracetag: %s\n", $tracetag) if ($tracetag ne "");
  91. printf("frsvsn: %s\n", $frsvsn) if ($frsvsn ne "");
  92. printf("cxtg: %s\n", $cxtg) if ($cxtg ne "");
  93. printf("replcxtion: %s\n", $replcxtion) if ($replcxtion ne "");
  94. printf("IdflagsSet: %08x\n", $idflagsset) if ($idflagsset ne "");
  95. printf("IdflagsClr: %08x\n", $idflagsclear) if ($idflagsclear ne "");
  96. $errorwindow = 100;
  97. $lastlineout = -$errorwindow;
  98. printf("\n\n\n");
  99. while (<>) {
  100. $linenumber++;
  101. if ($InFile ne $ARGV) {
  102. $InFile = $ARGV;
  103. printf("- - - - - %s - - - - -\n\n", $InFile);
  104. }
  105. @buf[++$bx & $bmax] = $_;
  106. ($func, $thrd, $line) = split(/:/, $_);
  107. #
  108. # Track per-thread linenumber for ++ records.
  109. #
  110. $thrdlinenumber[$thrd]++;
  111. #
  112. # Output continuation "++" record if it follows a line already put out
  113. # by the same thread.
  114. #
  115. if ((($lastthrdline[$thrd] - $thrdlinenumber[$thrd]) == -1) && m/\> \+\+/) {
  116. &MakeThreadSpace2();
  117. printf("%s", $_);
  118. }
  119. #
  120. # Capture error / warning messages.
  121. #
  122. elsif ((($linenumber - $lastlineout) < $errorwindow) &&
  123. m/ntstatus|wstatus|fstatus|error|assertion|warn|fail/i &&
  124. !m/The key was not found/ &&
  125. !m/Table Already Closed/ &&
  126. !m/ReadUsnJournalData - NTStatus 00000103/ &&
  127. !m/ReadUsnJournalData - NTStatus 00000000/) {
  128. &MakeThreadSpace2();
  129. printf("%s", $_);
  130. }
  131. #
  132. # error window above only applies to error messages discovered after some other line
  133. # that had been selected & printed. This test gets any error related lines that also
  134. # happen to match the filename or the co guid which could occur outside the error
  135. # window and before the next line selected by other tests.
  136. #
  137. elsif (m/ntstatus|wstatus|fstatus|error|assertion|warn|fail/i &&
  138. ((($fname ne "") && m/$fname/o) ||
  139. (($cog ne "") && m/$cog/io ))) {
  140. &MakeThreadSpace2();
  141. printf("%s", $_);
  142. }
  143. if (m/assertion/i) {
  144. &MakeThreadSpace();
  145. printf("%s", $_);
  146. }
  147. #
  148. # capture config parameters
  149. #
  150. # if (m/^(<GetConfigParam|<FRSMAIN)/) {$confbuf[$confbufx++] = $_; next;}
  151. if (m/Compile Date/) {$confhash{"Compile Date"} = $_; next;}
  152. #
  153. # build table of join guid v.s. cxtion.
  154. #
  155. if (m/JOINED /) {
  156. if (m/JOINED *([0123456789abcdef]*) /i) {
  157. $joinhash{$1} = $_;
  158. next;
  159. } else {
  160. &MakeThreadSpace();
  161. printf("%s", $_);
  162. }
  163. }
  164. #
  165. # capture guid - fid translations.
  166. #
  167. if (m/^<DbsFidToGuid/ || m/^<DbsGuidToFid/ )
  168. {$recbuf[$reconcilex++] = $_; next;}
  169. #
  170. # capture JrnlGetFileCoLocationCmd records
  171. #
  172. # if (m/^<JrnlGetFileCoLocationCmd/ ||
  173. # m/<<< IN/) {
  174. # $getlocn[$getlocnx++] = $_;
  175. # next;
  176. # }
  177. #
  178. # capture VVPrint records
  179. #
  180. if (($frsvsn ne "") && ((m/^<VVPrint/) || (m/ VV_PRINT/))) {
  181. &GatherVVprint();
  182. next;
  183. }
  184. #
  185. # look for CO Trace record
  186. #
  187. if (m/:: CoG/) {
  188. &GatherCOTrace();
  189. next;
  190. }
  191. #
  192. # look for Usn Trace record
  193. #
  194. if (m/:U:/) {
  195. &GatherUsnTrace();
  196. next;
  197. }
  198. #
  199. # look for a connection Trace record
  200. #
  201. if ((($cxtg ne "") || ($replcxtion ne "")) && m/:X: |:S: |Cxtion state change|Cxtion .* state change from/) {
  202. &GatherCxtgTrace();
  203. next;
  204. }
  205. #
  206. # look for start of Change Order Entry
  207. #
  208. if (m/\.\.\.CHANGE_ORDER_ENTRY_TYPE\.\.\./) {
  209. ++$costart; $cothrd[$thrd]=1;
  210. }
  211. if (($costart > 0) && ($cothrd[$thrd] == 1)) {
  212. &GatherCOE();
  213. next;
  214. }
  215. #
  216. # look for start of IDTable
  217. #
  218. if ((m/Data Record for Table: \.\.\.IDTable/) && !m/<DbsUpdateIDTableFields/) {
  219. ++$idstart; $idthrd[$thrd]=1;
  220. }
  221. if (($idstart > 0) && ($idthrd[$thrd] == 1)) {
  222. &GatherIDT();
  223. next;
  224. }
  225. #
  226. # look for start of DIRTable
  227. #
  228. if (m/Data Record for Table: \.\.\.DIRTable/) {
  229. ++$dirstart; $dirthrd[$thrd]=1;
  230. }
  231. if (($dirstart > 0) && ($dirthrd[$thrd] == 1)) {
  232. &GatherDIRT();
  233. next;
  234. }
  235. #
  236. # look for start of OUTLOGTable
  237. #
  238. if (m/Data Record for Table: \.\.\.OUTLOGTable/) {
  239. ++$olstart; $olthrd[$thrd]=1;
  240. }
  241. if (($olstart > 0) && ($olthrd[$thrd] == 1)) {
  242. &GatherOLT();
  243. next;
  244. }
  245. #
  246. # look for start of INLOGTable
  247. #
  248. if (m/Data Record for Table: \.\.\.INLOGTable/) {
  249. ++$ilstart; $ilthrd[$thrd]=1; $DisplayRetry[$thrd]=0;
  250. }
  251. if (($ilstart > 0) && ($ilthrd[$thrd] == 1)) {
  252. &GatherILT();
  253. }
  254. #
  255. # If this was the retry thread and we printed the record then also
  256. # show what happened to it.
  257. #
  258. if (m/^<ChgOrdRetryWorker/ && ($DisplayRetry[$thrd] == 1)) {
  259. &MakeThreadSpace2();
  260. printf("%s", $_);
  261. }
  262. #
  263. # look for start of REPLICA_TYPE
  264. #
  265. # if (m/Display for Node: \.\.\.REPLICA_TYPE/) {
  266. # ++$rtstart; $rtthrd[$thrd]=1;
  267. # }
  268. # if (($rtstart > 0) && ($rtthrd[$thrd] == 1)) {
  269. # &GatherRTNode();
  270. # }
  271. }
  272. #
  273. # Dump out misc accumulated info.
  274. #
  275. printf("\n\n\nConfiguration params:\n\n");
  276. foreach $param (sort keys(%confhash)) {
  277. printf("%s", $confhash{$param});
  278. }
  279. printf("\n\n");
  280. for ($j=0; $j < $confbufx; $j++) {
  281. printf("%s", $confbuf[$j]);
  282. }
  283. printf("\n\n Joined cxtions with cxtion guid:\n\n");
  284. foreach $param (sort keys(%$joinhash)) {
  285. printf("%s", $joinhash{$param});
  286. }
  287. sub GatherVVprint {
  288. #
  289. # Collect VVPRINT records for this thread.
  290. # The next :: record on this thread will print them if a match was found.
  291. #
  292. $vvp[$thrd][$vvpx[$thrd]++] = $_;
  293. if (!m/:: CoG/) {
  294. if (m/$frsvsn/i) {
  295. $fvvp[$thrd] = 1;
  296. $lastlineout = $linenumber;
  297. }
  298. } else {
  299. $vvpx[$thrd]--;
  300. #
  301. # Print vvprint capture buffer. Empty the buffer.
  302. #
  303. &MakeThreadSpace();
  304. for ($j=0; $j < $vvpx[$thrd]; $j++) {
  305. printf("%s", $vvp[$thrd][$j]);
  306. }
  307. }
  308. }
  309. sub GatherCOTrace {
  310. #
  311. # Check for a prior collection of VVPRINT records on this thread.
  312. # Print them if a match was found. Empty the capture buffer.
  313. #
  314. if ($frsvsn ne "") {
  315. if ($fvvp[$thrd] == 1) {
  316. &GatherVVprint();
  317. }
  318. $vvpx[$thrd] = 0;
  319. $fvvp[$thrd] = 0;
  320. }
  321. #
  322. # check this CO Trace record against filter.
  323. #
  324. if ((($fid ne "") && m/FID $fid/io) ||
  325. (($fname ne "") && m/FN: $fname/o) ||
  326. (($tracetag ne "") && m/\[$tracetag/o) ||
  327. (($cog ne "") && m/CoG $cog/io)) {
  328. # ($TraceCog) = m/CoG (........)/;
  329. #
  330. # dump accumulated guid-fid records.
  331. #
  332. &MakeThreadSpace();
  333. for ($i=0; $i<$reconcilex; $i++) {printf("%s", $recbuf[$i]);}
  334. printf("%s", $_);
  335. }
  336. $reconcilex = 0;
  337. }
  338. sub GatherUsnTrace {
  339. #
  340. # check this CO Trace record against filter.
  341. #
  342. if ((($fid ne "") && m/Fid $fid/io) ||
  343. (($pfid ne "") && m/PFid $pfid/io) ||
  344. (($fname ne "") && m/$fname/o)) {
  345. #
  346. # dump accumulated locn cmd results.
  347. #
  348. &MakeThreadSpace();
  349. for ($i=0; $i<$getlocnx; $i++) {printf("%s", $getlocn[$i]);}
  350. printf("%s", $_);
  351. }
  352. $getlocnx = 0;
  353. }
  354. sub GatherCxtgTrace {
  355. #
  356. # check this connection Trace record (:X: or :S:) against filter.
  357. #
  358. if ((($cxtg ne "") && m/ :X: $cxtg/io) ||
  359. (($cxtg ne "") && m/Cxtion.*$cxtg/io) ||
  360. (($replcxtion ne "") && m/$replcxtion/o)) {
  361. &MakeThreadSpace2();
  362. printf("%s", $_);
  363. }
  364. }
  365. sub GatherCOE {
  366. $co{$thrd, $cox[$thrd]++} = $_;
  367. #
  368. # check this CO against filter.
  369. #
  370. if ((($fid ne "") && m/FileRef: $fid/io) ||
  371. (($fname ne "") && m/FileName.*$fname/io) ||
  372. (($pfid ne "") && m/ParentRef: $pfid/io) ||
  373. (($frsvsn ne "") && m/FrsVsn: $frsvsn/io) ||
  374. (($fguid ne "") && m/FileGuid : $fguid-/io) ||
  375. (($pfguid ne "") && m/OParGuid : $pfguid-/io) ||
  376. (($pfguid ne "") && m/NParGuid : $pfguid-/io) ||
  377. (($cog ne "") && m/Address.*$cog-/io)) {
  378. $fcoprint[$thrd] = 1;
  379. $lastlineout = $linenumber;
  380. }
  381. #
  382. # End of CO?
  383. #
  384. if (m/> Version:/) {
  385. if ($fcoprint[$thrd] == 1) {
  386. &MakeThreadSpace();
  387. for ($i=0; $i<$cox[$thrd]; $i++) {
  388. printf("%s", $co{$thrd, $i});
  389. }
  390. }
  391. $cothrd[$thrd] = 0;
  392. $cox[$thrd] = 0;
  393. $fcoprint[$thrd] = 0;
  394. --$costart;
  395. }
  396. }
  397. sub GatherIDT {
  398. # skip spare fields except for Spare1Bin
  399. #
  400. if (m/Spare1Bin / || !(m/Spare/)) {$id{$thrd, $idx[$thrd]++} = $_;}
  401. #
  402. # check this IDTable entry against filter.
  403. #
  404. if ((($fid ne "") && m/ FileID .*$fid/io) ||
  405. (($fname ne "") && m/FileName .*$fname/io) ||
  406. (($frsvsn ne "") && m/OriginatorVSN .*$frsvsn/io) ||
  407. (($pfid ne "") && m/ParentFileID .*$pfid/io) ||
  408. (($fguid ne "") && m/FileGuid .*$fguid-/io) ||
  409. (($pfguid ne "") && m/ParentGuid .*$pfguid-/io)) {
  410. $fidprint[$thrd] = 1;
  411. $lastlineout = $linenumber;
  412. }
  413. if (m/Flags /) {($idflags[$thrd]) = m/Flags.*, (........)/;}
  414. if (m/Spare1Bin /) {
  415. #
  416. # End of IDTable. Dump it out if filter matched
  417. #
  418. if (($fidprint[$thrd] == 1) &&
  419. ((($idflags[$thrd] & $idflagsset) != 0) || (($idflags[$thrd] & $idflagsclear) == 0))
  420. ) {
  421. &MakeThreadSpace();
  422. for ($i=0; $i<$idx[$thrd]; $i++) {
  423. printf("%s", $id{$thrd, $i});
  424. }
  425. }
  426. $idthrd[$thrd] = 0;
  427. $idx[$thrd] = 0;
  428. $fidprint[$thrd] = 0;
  429. $idflags[$thrd] = 0;
  430. --$idstart;
  431. }
  432. }
  433. sub GatherDIRT {
  434. $dir{$thrd, $dirx[$thrd]++} = $_;
  435. #
  436. # check this DIRTable entry against filter.
  437. #
  438. if ((($fid ne "") && m/DFileID .*$fid/io) ||
  439. (($fname ne "") && m/DFileName .*$fname/io) ||
  440. (($pfid ne "") && m/DParentFileID .*$pfid/io) ||
  441. (($fguid ne "") && m/DFileGuid .*$fguid-/io) ||
  442. (($pfguid ne "") && m/DParentGuid .*$pfguid-/io)) {
  443. $fdirprint[$thrd] = 1;
  444. $lastlineout = $linenumber;
  445. }
  446. if (m/DFileName /) {
  447. #
  448. # End of DIRTable. Dump it out if filter matched
  449. #
  450. if ($fdirprint[$thrd] == 1) {
  451. &MakeThreadSpace();
  452. for ($i=0; $i<$dirx[$thrd]; $i++) {
  453. printf("%s", $dir{$thrd, $i});
  454. }
  455. }
  456. $dirthrd[$thrd] = 0;
  457. $dirx[$thrd] = 0;
  458. $fdirprint[$thrd] = 0;
  459. --$dirstart;
  460. }
  461. }
  462. sub GatherOLT {
  463. # skip spare fields except for Spare1Bin
  464. #
  465. if (m/Spare1Bin / || !(m/Spare/)) {$ol{$thrd, $olx[$thrd]++} = $_;}
  466. #
  467. # check this OUTLOGTable entry against filter.
  468. #
  469. if ((($fname ne "") && m/FileName .*$fname/io) ||
  470. (($frsvsn ne "") && m/FrsVsn .*$frsvsn/io) ||
  471. (($fguid ne "") && m/FileGuid .*$fguid-/io) ||
  472. (($cog ne "") && m/ChangeOrderGuid .*$cog-/io) ||
  473. (($pfguid ne "") && m/ParentGuid .*$pfguid-/io)) {
  474. $folprint[$thrd] = 1;
  475. $lastlineout = $linenumber;
  476. }
  477. if (m/Flags /) {($olflags[$thrd]) = m/Flags.*, (........)/;}
  478. if (m/FileName /) {
  479. #
  480. # End of OLTable. Dump it out if filter matched
  481. #
  482. if ($folprint[$thrd] == 1) {
  483. &MakeThreadSpace();
  484. for ($i=0; $i<$olx[$thrd]; $i++) {
  485. printf("%s", $ol{$thrd, $i});
  486. }
  487. }
  488. $olthrd[$thrd] = 0;
  489. $olx[$thrd] = 0;
  490. $folprint[$thrd] = 0;
  491. --$olstart;
  492. }
  493. }
  494. sub GatherILT {
  495. #
  496. # Skip spare fields except for Spare1Bin.
  497. #
  498. if (m/Spare1Bin / || !(m/Spare/)) {$il{$thrd, $ilx[$thrd]++} = $_;}
  499. #
  500. # check this INLOGTable entry against filter.
  501. #
  502. if ((($fname ne "") && m/FileName .*$fname/io) ||
  503. (($frsvsn ne "") && m/FrsVsn .*$frsvsn/io) ||
  504. (($fguid ne "") && m/FileGuid .*$fguid-/io) ||
  505. (($cog ne "") && m/ChangeOrderGuid .*$cog-/io) ||
  506. (($pfguid ne "") && m/ParentGuid .*$pfguid-/io)) {
  507. $filprint[$thrd] = 1;
  508. $lastlineout = $linenumber;
  509. }
  510. if (m/Flags /) {($ilflags[$thrd]) = m/Flags.*, (........)/;}
  511. if (m/FileName /) {
  512. #
  513. # End of ILTable. Dump it out if filter matched
  514. #
  515. if ($filprint[$thrd] == 1) {
  516. &MakeThreadSpace();
  517. for ($i=0; $i<$ilx[$thrd]; $i++) {
  518. printf("%s", $il{$thrd, $i});
  519. }
  520. #
  521. # if retry thread, display retry thread results.
  522. #
  523. if (m/^<ChgOrdRetryWorker/) {$DisplayRetry[$thrd] = 1;}
  524. }
  525. $ilthrd[$thrd] = 0;
  526. $ilx[$thrd] = 0;
  527. $filprint[$thrd] = 0;
  528. --$ilstart;
  529. }
  530. }
  531. sub GatherRTNode {
  532. # tbs
  533. if (!(m/> ------------------/)) {
  534. $rtnode[$thrd][$rtnodex[$thrd]++] = $_;
  535. }
  536. #
  537. # save the replica name
  538. #
  539. if (m/ Name .*: (.*) /) {$rtnodename[$thrd] = $1;}
  540. }
  541. sub MakeThreadSpace {
  542. #
  543. # put in a could blank lines if the thread id has changed.
  544. #
  545. if ($thrd != $lastthrdprint) {printf("\n\n");}
  546. $lastlineout = $linenumber;
  547. $lastthrdprint = $thrd;
  548. #
  549. # Track last line out by thread.
  550. #
  551. $lastthrdline[$thrd] = $thrdlinenumber[$thrd];
  552. }
  553. sub MakeThreadSpace2 {
  554. #
  555. # put in a could blank lines if the thread id has changed.
  556. # BUT don't update $lastlineout since it can cause too much error spew.
  557. #
  558. if ($thrd != $lastthrdprint) {printf("\n\n");}
  559. $lastthrdprint = $thrd;
  560. #
  561. # Track last line out by thread.
  562. #
  563. $lastthrdline[$thrd] = $thrdlinenumber[$thrd];
  564. }
  565. __END__
  566. :endofperl
  567. @perl %0.cmd %*