1 /*
2 * nsfProfile.c --
3 *
4 * Provides profiling on Next Scripting Framework internals.
5 * For turning on profiling, NSF_PROFILE must be configured.
6 *
7 * Copyright (C) 2010-2017 Gustaf Neumann
8 *
9 * Vienna University of Economics and Business
10 * Institute of Information Systems and New Media
11 * A-1020, Welthandelsplatz 1
12 * Vienna, Austria
13 *
14 * This work is licensed under the MIT License https://www.opensource.org/licenses/MIT
15 *
16 * Copyright:
17 *
18 * Permission is hereby granted, free of charge, to any person obtaining a
19 * copy of this software and associated documentation files (the "Software"),
20 * to deal in the Software without restriction, including without limitation
21 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
22 * and/or sell copies of the Software, and to permit persons to whom the
23 * Software is furnished to do so, subject to the following conditions:
24 *
25 * The above copyright notice and this permission notice shall be included in
26 * all copies or substantial portions of the Software.
27 *
28 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
29 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
30 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
31 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
32 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
33 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
34 * DEALINGS IN THE SOFTWARE.
35 *
36 */
37
38 #include "nsfInt.h"
39
40 #if defined(NSF_PROFILE)
41 typedef struct NsfProfileData {
42 long microSec;
43 long count;
44 } NsfProfileData;
45 #endif
46
47 /*
48 *----------------------------------------------------------------------
49 * NsfProfileObjectLabel, NsfProfileMethodLabel --
50 *
51 * Produce a string label for an object or method using in profiling.
52 * NsfProfileMethodLabel() is available also when compiled without
53 * NSF_PROFILE.
54 *
55 * Results:
56 * None
57 *
58 * Side effects:
59 * Initializes and fills the passed Tcl_DString,
60 *
61 *----------------------------------------------------------------------
62 */
63 static void NsfProfileObjectLabel(Tcl_DString *dsPtr, NsfObject *object)
64 nonnull(1) nonnull(2);
65
66 static void
NsfProfileObjectLabel(Tcl_DString * dsPtr,NsfObject * object)67 NsfProfileObjectLabel(Tcl_DString *dsPtr, NsfObject *object) {
68
69 nonnull_assert(dsPtr != NULL);
70 nonnull_assert(object != NULL);
71
72 Tcl_DStringAppend(dsPtr, ObjectName_(object), -1);
73 Tcl_DStringAppend(dsPtr, " ", 1);
74 Tcl_DStringAppend(dsPtr, ClassName(object->cl), -1);
75 }
76
77 static void NsfProfileMethodLabel(Tcl_DString *dsPtr, NsfClass *class, const char *methodName)
78 nonnull(1) nonnull(3);
79
80 static void
NsfProfileMethodLabel(Tcl_DString * dsPtr,NsfClass * class,const char * methodName)81 NsfProfileMethodLabel(Tcl_DString *dsPtr, NsfClass *class, const char *methodName) {
82
83 nonnull_assert(dsPtr != NULL);
84 nonnull_assert(methodName != NULL);
85
86 Tcl_DStringAppendElement(dsPtr, methodName);
87 if (class != NULL) {
88 Tcl_DStringAppend(dsPtr, " ", 1);
89 Tcl_DStringAppend(dsPtr, ObjStr(class->object.cmdName), -1);
90 }
91
92 }
93
94
95 /*
96 *----------------------------------------------------------------------
97 * NsfProfileDeprecatedCall --
98 *
99 * Output a line in case a deprecated function/method is called using
100 * the low-level NsfDeprecatedCmd() function.
101 *
102 * Results:
103 * None
104 *
105 * Side effects:
106 * logging
107 *
108 *----------------------------------------------------------------------
109 */
110 void
NsfProfileDeprecatedCall(Tcl_Interp * interp,NsfObject * UNUSED (object),NsfClass * class,const char * methodName,const char * altMethod)111 NsfProfileDeprecatedCall(Tcl_Interp *interp, NsfObject *UNUSED(object), NsfClass *class,
112 const char *methodName, const char *altMethod) {
113 Tcl_DString ds;
114
115 nonnull_assert(interp != NULL);
116 //nonnull_assert(object != NULL);
117 nonnull_assert(methodName != NULL);
118 nonnull_assert(altMethod != NULL);
119
120 Tcl_DStringInit(&ds);
121 Tcl_DStringAppend(&ds, "{", 1);
122 NsfProfileMethodLabel(&ds, class, methodName);
123 Tcl_DStringAppend(&ds, "}", 1);
124
125 NsfDeprecatedCmd(interp, "method", ds.string, altMethod);
126 Tcl_DStringFree(&ds);
127 }
128
129 /*
130 *----------------------------------------------------------------------
131 * NsfProfileDebugCall, NsfProfileDebugExit --
132 *
133 * Output a line in case a function/method is called/exited having the
134 * debug flag set. These two functions use the Tcl commands ::nsf::debug::call
135 * and ::nsf::debug::exit for reporting.
136 *
137 * Results:
138 * None
139 *
140 * Side effects:
141 * logging
142 *
143 *----------------------------------------------------------------------
144 */
145 void
NsfProfileDebugCall(Tcl_Interp * interp,NsfObject * object,NsfClass * class,const char * methodName,int objc,Tcl_Obj ** objv)146 NsfProfileDebugCall(Tcl_Interp *interp, NsfObject *object, NsfClass *class, const char *methodName,
147 int objc, Tcl_Obj **objv) {
148 NsfRuntimeState *rst;
149 Tcl_Obj *listObj;
150 Tcl_DString ds;
151
152 nonnull_assert(interp != NULL);
153 nonnull_assert(methodName != NULL);
154
155 rst = RUNTIME_STATE(interp);
156 rst->debugCallingDepth++;
157
158 Tcl_DStringInit(&ds);
159 Nsf_DStringPrintf(&ds, "::nsf::debug::call %d {", rst->debugCallingDepth);
160 if (object != NULL) {
161 NsfProfileObjectLabel(&ds, object);
162 }
163 Tcl_DStringAppend(&ds, "} {", 3);
164 NsfProfileMethodLabel(&ds, class, methodName);
165 Tcl_DStringAppend(&ds, "}", 1);
166
167 listObj = Tcl_NewListObj(objc, objv);
168 INCR_REF_COUNT(listObj);
169 Nsf_DStringPrintf(&ds, " {%s}", ObjStr(listObj));
170 DECR_REF_COUNT(listObj);
171
172 NsfDStringEval(interp, &ds, "debug call", (NSF_EVAL_DEBUG|NSF_EVAL_SAVE|NSF_EVAL_NOPROFILE));
173
174 Tcl_DStringFree(&ds);
175
176 }
177
178 void
NsfProfileDebugExit(Tcl_Interp * interp,NsfObject * object,NsfClass * class,const char * methodName,long startSec,long startUsec)179 NsfProfileDebugExit(Tcl_Interp *interp, NsfObject *object, NsfClass *class, const char *methodName,
180 long startSec, long startUsec) {
181 Tcl_DString ds, *dsPtr = &ds;
182 NsfRuntimeState *rst;
183
184 nonnull_assert(interp != NULL);
185 nonnull_assert(methodName != NULL);
186
187 rst = RUNTIME_STATE(interp);
188 Tcl_DStringInit(dsPtr);
189 Nsf_DStringPrintf(dsPtr, "::nsf::debug::exit %d {", rst->debugCallingDepth);
190
191 if (object != NULL) {
192 NsfProfileObjectLabel(dsPtr, object);
193 }
194 Tcl_DStringAppend(dsPtr, "} {", 3);
195 NsfProfileMethodLabel(dsPtr, class, methodName);
196 Tcl_DStringAppend(dsPtr, "} ", 1);
197 Tcl_DStringAppendElement(dsPtr, ObjStr(Tcl_GetObjResult(interp)));
198
199 if (startSec != 0 || startUsec != 0) {
200 struct Tcl_Time trt;
201
202 Tcl_GetTime(&trt);
203 Nsf_DStringPrintf(dsPtr, " %ld ", (trt.sec - startSec) * 1000000 + (trt.usec - startUsec));
204 } else {
205 Tcl_DStringAppend(dsPtr, " {}", 4);
206 }
207
208 NsfDStringEval(interp, &ds, "debug exit", (NSF_EVAL_DEBUG|NSF_EVAL_SAVE|NSF_EVAL_NOPROFILE));
209
210 Tcl_DStringFree(dsPtr);
211 rst->debugCallingDepth--;
212 }
213
214
215 #if defined(NSF_PROFILE)
216 /*
217 *----------------------------------------------------------------------
218 * ReportLine --
219 *
220 * Report a profile line via NsfLog(). Since NsfLog() uses a Tcl function,
221 * ReportLine has to turn off profiling to avoid recursive profile
222 * invocation. It is as well necessary to save the interp result.
223 *
224 * Results:
225 * None
226 *
227 * Side effects:
228 * logging
229 *
230 *----------------------------------------------------------------------
231 */
232 static void ReportLine(Tcl_Interp *interp, int level, NsfRuntimeState *rst, const char *line)
233 nonnull(1) nonnull(3) nonnull(4);
234
235 static void
ReportLine(Tcl_Interp * interp,int level,NsfRuntimeState * rst,const char * line)236 ReportLine(Tcl_Interp *interp, int level, NsfRuntimeState *rst, const char *line) {
237 Tcl_Obj *savedResultObj;
238 int prevProfileSetting;
239
240 nonnull_assert(interp != NULL);
241 nonnull_assert(rst != NULL);
242 nonnull_assert(line != NULL);
243
244 prevProfileSetting = rst->doProfile;
245 rst->doProfile = 0;
246
247 savedResultObj = Tcl_GetObjResult(interp);
248 INCR_REF_COUNT(savedResultObj);
249
250 NsfLog(interp, level, "%s", line);
251
252 Tcl_SetObjResult(interp, savedResultObj);
253 DECR_REF_COUNT(savedResultObj);
254
255 rst->doProfile = prevProfileSetting;
256 }
257
258 /*
259 *----------------------------------------------------------------------
260 * NsfProfileFillTable --
261 *
262 * Insert or Update a keyed entry with provided microseconds and
263 * update the counts for this entry.
264 *
265 * Results:
266 * None
267 *
268 * Side effects:
269 * Updated or created profile data entry
270 *
271 *----------------------------------------------------------------------
272 */
273 static void NsfProfileFillTable(Tcl_HashTable *table, const char *keyStr, double totalMicroSec)
274 nonnull(1) nonnull(2);
275
276 static void
NsfProfileFillTable(Tcl_HashTable * table,const char * keyStr,double totalMicroSec)277 NsfProfileFillTable(Tcl_HashTable *table, const char *keyStr, double totalMicroSec) {
278 NsfProfileData *value;
279 Tcl_HashEntry *hPtr;
280 int isNew;
281
282 nonnull_assert(table != NULL);
283 nonnull_assert(keyStr != NULL);
284
285 hPtr = Tcl_CreateHashEntry(table, keyStr, &isNew);
286 if (isNew != 0) {
287 value = (NsfProfileData *)ckalloc(sizeof(NsfProfileData));
288 value->microSec = 0;
289 value->count = 0;
290 Tcl_SetHashValue(hPtr, (ClientData) value);
291 } else {
292 value = (NsfProfileData *)Tcl_GetHashValue (hPtr);
293 }
294 value->microSec += (long)totalMicroSec;
295 value->count ++;
296 }
297
298 /*
299 *----------------------------------------------------------------------
300 * Nsf_ProfileFilterObjCmd --
301 *
302 * Stub command to include C-level commands in profile traces.
303 *
304 * Results:
305 * Tcl result code
306 *
307 * Side effects:
308 * Perform tracing
309 *
310 *----------------------------------------------------------------------
311 */
312 static int
Nsf_ProfileFilterObjCmd(ClientData cd,Tcl_Interp * interp,int objc,Tcl_Obj * const objv[])313 Nsf_ProfileFilterObjCmd(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) {
314 NsfShadowTclCommandInfo *ti;
315 int result;
316 struct Tcl_Time start;
317 const char *fullMethodName, *label;
318 Tcl_DString ds;
319
320 assert(cd != NULL);
321
322 fullMethodName = ObjStr(objv[0]);
323 ti = (NsfShadowTclCommandInfo *)cd;
324
325 if (ti->nrArgs == 0 || objc < 2) {
326 label = fullMethodName;
327 } else {
328 int i, nrArgs = objc;
329
330 if (nrArgs > ti->nrArgs) {
331 nrArgs = ti->nrArgs;
332 }
333
334 Tcl_DStringInit(&ds);
335 Tcl_DStringAppend(&ds, fullMethodName, -1);
336 for (i = 1; i<=nrArgs; i++) {
337 Tcl_DStringAppend(&ds, " ", 1);
338 Tcl_DStringAppend(&ds, ObjStr(objv[i]), -1);
339 }
340 label = ds.string;
341 }
342
343 NsfProfileTraceCallAppend(interp, label);
344
345 Tcl_GetTime(&start);
346 result = Tcl_NRCallObjProc(interp, ti->proc, ti->clientData, objc, objv);
347 NsfProfileRecordProcData(interp, label, start.sec, start.usec);
348
349 if (label != fullMethodName) {
350 Tcl_DStringFree(&ds);
351 }
352 return result;
353 }
354
355 /*
356 *----------------------------------------------------------------------
357 * GetPair --
358 *
359 * Split a Tcl_Obj into a nameObj and an integer value, if possible
360 *
361 * Results:
362 * Tcl result
363 *
364 * Side effects:
365 * Produce warnings for error cases, when "verbose" is on.
366 *
367 *----------------------------------------------------------------------
368 */
369 static int
370 GetPair(Tcl_Interp *interp, Tcl_Obj *objPtr, int verbose, Tcl_Obj **nameObjPtr, int *nrArgsPtr)
371 nonnull(1) nonnull(2) nonnull(4) nonnull(5);
372
373 static int
GetPair(Tcl_Interp * interp,Tcl_Obj * objPtr,int verbose,Tcl_Obj ** nameObjPtr,int * nrArgsPtr)374 GetPair(Tcl_Interp *interp, Tcl_Obj *objPtr, int verbose, Tcl_Obj **nameObjPtr, int *nrArgsPtr) {
375 int result = TCL_OK, oc;
376 Tcl_Obj **ov;
377
378 if (Tcl_ListObjGetElements(interp, objPtr, &oc, &ov) != TCL_OK) {
379 if (verbose) {
380 NsfLog(interp, NSF_LOG_WARN, "nsfprofile: invalid list element '%s'", ObjStr(objPtr));
381 result = TCL_ERROR;
382 }
383 } else {
384 if (oc == 1) {
385 *nameObjPtr = ov[0];
386 } else if (oc == 2) {
387 if (Tcl_GetIntFromObj(interp, ov[1], nrArgsPtr) == TCL_OK) {
388 *nameObjPtr = ov[0];
389 } else {
390 if (verbose) {
391 NsfLog(interp, NSF_LOG_WARN, "nsfprofile: second element of '%s' must be an integer", ObjStr(objPtr));
392 result = TCL_ERROR;
393 }
394 }
395 } else {
396 if (verbose) {
397 NsfLog(interp, NSF_LOG_WARN, "nsfprofile: list element '%s' not a valid pair", ObjStr(objPtr));
398 result = TCL_ERROR;
399 }
400 }
401 }
402
403 return result;
404 }
405
406 /*
407 *----------------------------------------------------------------------
408 * NsfProfileTrace --
409 *
410 * Function to control trace behavior callable via Tcl.
411 *
412 * Results:
413 * OK
414 *
415 * Side effects:
416 * update RUNTIME_STATE(interp)->doTrace
417 * and profilePtr->verbose
418 * and profilePtr->inmemory
419 *
420 *----------------------------------------------------------------------
421 */
422 int
NsfProfileTrace(Tcl_Interp * interp,int withEnable,int withVerbose,int withDontsave,Tcl_Obj * builtinObjs)423 NsfProfileTrace(Tcl_Interp *interp, int withEnable, int withVerbose, int withDontsave, Tcl_Obj *builtinObjs) {
424 NsfRuntimeState *rst;
425 NsfProfile *profilePtr;
426 int oldProfileState, oc;
427 Tcl_Obj **ov;
428
429 nonnull_assert(interp != NULL);
430
431 rst = RUNTIME_STATE(interp);
432 profilePtr = &rst->profile;
433
434 oldProfileState = rst->doTrace;
435 rst->doTrace = withEnable;
436
437 /*
438 * Turn automatically profiling on&off, when trace is turned on/off
439 */
440 if (withEnable == 1) {
441 if (rst->doProfile == 1) {
442 NsfLog(interp, NSF_LOG_WARN, "nsfprofile: tracing is already active");
443 } else {
444 /*
445 * Activate profile trace.
446 */
447 if (builtinObjs != NULL) {
448 /*
449 * A list of commands was provided
450 */
451 if (Tcl_ListObjGetElements(interp, builtinObjs, &oc, &ov) != TCL_OK) {
452 NsfLog(interp, NSF_LOG_WARN, "nsfprofile: argument '%s' is not a list of commands", ObjStr(builtinObjs));
453 } else {
454 int i;
455 NsfShadowTclCommandInfo *ti = NEW_ARRAY(NsfShadowTclCommandInfo, oc);
456
457 for (i = 0; i < oc; i++) {
458 int nrArgs = 0;
459 Tcl_Obj *nameObj = NULL;
460
461 if (GetPair(interp, ov[i], 1, &nameObj, &nrArgs) == TCL_OK) {
462 assert(nameObj != NULL);
463 ti[i].nrArgs = nrArgs;
464
465 if (NsfReplaceCommand(interp, nameObj, Nsf_ProfileFilterObjCmd, &ti[i], &ti[i]) != TCL_OK) {
466 NsfLog(interp, NSF_LOG_WARN, "nsfprofile: list element '%s' is not a command", ObjStr(nameObj));
467 }
468 }
469 }
470 INCR_REF_COUNT(builtinObjs);
471 profilePtr->shadowedObjs = builtinObjs;
472 profilePtr->shadowedTi = ti;
473 }
474 }
475 }
476 } else {
477 /*
478 * Deactivate profile trace.
479 */
480 if (profilePtr->shadowedObjs != NULL) {
481
482 if (Tcl_ListObjGetElements(interp, profilePtr->shadowedObjs, &oc, &ov) != TCL_OK) {
483 NsfLog(interp, NSF_LOG_WARN, "nsfprofile: shadowed objects are apparently not a list");
484 } else {
485 int i;
486
487 for (i = 0; i < oc; i++) {
488 int nrArgs = 0;
489 Tcl_Obj *nameObj = NULL;
490
491 if (GetPair(interp, ov[i], 0, &nameObj, &nrArgs) == TCL_OK) {
492 assert(nameObj != NULL);
493 NsfReplaceCommandCleanup(interp, nameObj, &profilePtr->shadowedTi[i]);
494 }
495 }
496 }
497 INCR_REF_COUNT(profilePtr->shadowedObjs);
498
499 FREE(NsfShadowTclCommandInfo*, profilePtr->shadowedTi);
500 profilePtr->shadowedTi = NULL;
501 profilePtr->shadowedObjs = NULL;
502 /*fprintf(stderr, "freed profile information\n");*/
503 }
504
505 }
506
507 rst->doProfile = withEnable;
508
509 profilePtr->verbose = withVerbose;
510 profilePtr->inmemory = (withDontsave == 1) ? 0 : 1;
511 Tcl_SetObjResult(interp, Tcl_NewBooleanObj(oldProfileState));
512
513 return TCL_OK;
514 }
515
516
517
518 /*
519 *----------------------------------------------------------------------
520 * NsfProfileTraceCallAppend, NsfProfileTraceExitAppend --
521 *
522 * Low level function to add entries to the trace Tcl_DString when functions ar
523 * called or exited.
524 *
525 * Results:
526 * None
527 *
528 * Side effects:
529 * update profilePtr->depth and profilePtr->traceDs
530 *
531 *----------------------------------------------------------------------
532 */
533 void
NsfProfileTraceCallAppend(Tcl_Interp * interp,const char * label)534 NsfProfileTraceCallAppend(Tcl_Interp *interp, const char *label) {
535 NsfRuntimeState *rst = RUNTIME_STATE(interp);
536 NsfProfile *profilePtr = &rst->profile;
537 Tcl_DString ds;
538
539 profilePtr->depth ++;
540
541 Tcl_DStringInit(&ds);
542 Nsf_DStringPrintf(&ds, "call(%d): %s", profilePtr->depth, label);
543 if (profilePtr->verbose) {
544 ReportLine(interp, NSF_LOG_NOTICE, rst, ds.string);
545 }
546 if (profilePtr->inmemory) {
547 Tcl_DStringAppend(&ds, "\n", 1);
548 Tcl_DStringAppend(&profilePtr->traceDs, ds.string, ds.length);
549 }
550 Tcl_DStringFree(&ds);
551 }
552
553 void
NsfProfileTraceExitAppend(Tcl_Interp * interp,const char * label,double duration)554 NsfProfileTraceExitAppend(Tcl_Interp *interp, const char *label, double duration) {
555 NsfRuntimeState *rst = RUNTIME_STATE(interp);
556 NsfProfile *profilePtr = &rst->profile;
557 Tcl_DString ds;
558
559 Tcl_DStringInit(&ds);
560 Nsf_DStringPrintf(&ds, "exit(%d): %s %.0f", profilePtr->depth, label, duration);
561 if (profilePtr->verbose) {
562 ReportLine(interp, NSF_LOG_NOTICE, rst, ds.string);
563 }
564 if (profilePtr->inmemory) {
565 Tcl_DStringAppend(&ds, "\n", 1);
566 Tcl_DStringAppend(&profilePtr->traceDs, ds.string, ds.length);
567 }
568 Tcl_DStringFree(&ds);
569
570 profilePtr->depth --;
571 }
572
573
574 /*
575 *----------------------------------------------------------------------
576 * NsfProfileTraceCall, NsfProfileTraceExit --
577 *
578 * Add entries to the trace Tcl_DString when methods/procs are called or
579 * exited. This function builds the labels for invocation strings in the
580 * same way as for profiling and calls the lower-level function, which does
581 * the recording.
582 *
583 * Results:
584 * None
585 *
586 * Side effects:
587 * update profilePtr->depth and profilePtr->traceDs
588 *
589 *----------------------------------------------------------------------
590 */
591
592 void
NsfProfileTraceCall(Tcl_Interp * interp,NsfObject * object,NsfClass * class,const char * methodName)593 NsfProfileTraceCall(Tcl_Interp *interp, NsfObject *object, NsfClass *class, const char *methodName) {
594 NsfRuntimeState *rst = RUNTIME_STATE(interp);
595
596 if (rst->doTrace) {
597 Tcl_DString ds, traceLabel;
598
599 Tcl_DStringInit(&ds);
600 NsfProfileObjectLabel(&ds, object);
601
602 Tcl_DStringInit(&traceLabel);
603 Tcl_DStringAppendElement(&traceLabel, Tcl_DStringValue(&ds));
604 Tcl_DStringAppend(&traceLabel, " ", 1);
605
606 Tcl_DStringSetLength(&ds, 0);
607 NsfProfileMethodLabel(&ds, class, methodName);
608 Tcl_DStringAppendElement(&traceLabel, Tcl_DStringValue(&ds));
609
610 NsfProfileTraceCallAppend(interp, Tcl_DStringValue(&traceLabel));
611 Tcl_DStringFree(&traceLabel);
612 Tcl_DStringFree(&ds);
613 }
614 }
615
616
617 void
NsfProfileTraceExit(Tcl_Interp * interp,NsfObject * object,NsfClass * class,const char * methodName,struct Tcl_Time * callTime)618 NsfProfileTraceExit(Tcl_Interp *interp, NsfObject *object, NsfClass *class, const char *methodName,
619 struct Tcl_Time *callTime) {
620 NsfRuntimeState *rst = RUNTIME_STATE(interp);
621
622 if (rst->doTrace) {
623 Tcl_DString ds, traceLabel;
624 double totalMicroSec;
625 struct Tcl_Time trt;
626
627 Tcl_GetTime(&trt);
628 totalMicroSec = (double)((trt.sec - callTime->sec) * 1000000 + (trt.usec - callTime->usec));
629
630 Tcl_DStringInit(&ds);
631 NsfProfileObjectLabel(&ds, object);
632
633 Tcl_DStringInit(&traceLabel);
634 Tcl_DStringAppendElement(&traceLabel, Tcl_DStringValue(&ds));
635 Tcl_DStringAppend(&traceLabel, " ", 1);
636
637 Tcl_DStringSetLength(&ds, 0);
638 NsfProfileMethodLabel(&ds, class, methodName);
639 Tcl_DStringAppendElement(&traceLabel, Tcl_DStringValue(&ds));
640
641 NsfProfileTraceExitAppend(interp, Tcl_DStringValue(&traceLabel), totalMicroSec);
642 Tcl_DStringFree(&traceLabel);
643 Tcl_DStringFree(&ds);
644 }
645 }
646
647 /*
648 *----------------------------------------------------------------------
649 * NsfProfileRecordMethodData --
650 *
651 * This function is invoked, when a call of a method ends. It
652 * records profiling information based on the provided call stack
653 * content and the caller. In particular, it records the time spent
654 * in an object (identified with an objectKey) and the time spent
655 * in the method (using methodKey).
656 *
657 * Results:
658 * None
659 *
660 * Side effects:
661 * Updated or created profile data entries
662 *
663 *----------------------------------------------------------------------
664 */
665 void
NsfProfileRecordMethodData(Tcl_Interp * interp,NsfCallStackContent * cscPtr)666 NsfProfileRecordMethodData(Tcl_Interp *interp, NsfCallStackContent *cscPtr) {
667 NsfRuntimeState *rst;
668 double totalMicroSec;
669 NsfObject *obj;
670 NsfClass *cl;
671 Tcl_DString methodKey, objectKey, methodInfo;
672 NsfProfile *profilePtr;
673 struct Tcl_Time trt;
674
675 nonnull_assert(interp != NULL);
676 nonnull_assert(cscPtr != NULL);
677
678 Tcl_GetTime(&trt);
679 rst = RUNTIME_STATE(interp);
680 profilePtr = &rst->profile;
681
682 totalMicroSec = (double)((trt.sec - cscPtr->startSec) * 1000000 + (trt.usec - cscPtr->startUsec));
683 profilePtr->overallTime += (long)totalMicroSec;
684
685 obj = cscPtr->self;
686 if (obj->teardown == 0 || !obj->id) {
687 return;
688 }
689
690 Tcl_DStringInit(&objectKey);
691 NsfProfileObjectLabel(&objectKey, obj);
692
693 Tcl_DStringInit(&methodInfo);
694 Tcl_DStringInit(&methodKey);
695 cl = cscPtr->cl;
696 NsfProfileMethodLabel(&methodInfo, cl, cscPtr->methodName);
697
698 if (rst->doTrace) {
699 Tcl_DString traceKey;
700
701 Tcl_DStringInit(&traceKey);
702 Tcl_DStringAppendElement(&traceKey, Tcl_DStringValue(&objectKey));
703 Tcl_DStringAppend(&traceKey, " ", 1);
704 Tcl_DStringAppendElement(&traceKey, Tcl_DStringValue(&methodInfo));
705 NsfProfileTraceExitAppend(interp, Tcl_DStringValue(&traceKey), totalMicroSec);
706 Tcl_DStringFree(&traceKey);
707 }
708
709 /*
710 * Append method to object key as needed by statistics (but not by trace)
711 */
712 Tcl_DStringAppendElement(&objectKey, cscPtr->methodName);
713
714 /*
715 * Build method key, containing actual method info and caller method info.
716 */
717 Tcl_DStringInit(&methodKey);
718 Tcl_DStringAppend(&methodKey, "{", 1);
719 Tcl_DStringAppend(&methodKey, Tcl_DStringValue(&methodInfo), Tcl_DStringLength(&methodInfo));
720 Tcl_DStringAppend(&methodKey, "}", 1);
721
722 {
723 NsfCallStackContent *cscPtrTop = NsfCallStackGetTopFrame(interp, NULL);
724 if (cscPtrTop != NULL) {
725 Tcl_DStringAppend(&methodKey, " {", 2);
726 NsfProfileMethodLabel(&methodKey, cscPtrTop->cl, cscPtrTop->methodName);
727 Tcl_DStringAppend(&methodKey, "}", 1);
728 } else {
729 Tcl_DStringAppend(&methodKey, " {}", 3);
730 }
731 }
732
733 NsfProfileFillTable(&profilePtr->objectData, Tcl_DStringValue(&objectKey), totalMicroSec);
734 NsfProfileFillTable(&profilePtr->methodData, Tcl_DStringValue(&methodKey), totalMicroSec);
735 Tcl_DStringFree(&objectKey);
736 Tcl_DStringFree(&methodKey);
737 Tcl_DStringFree(&methodInfo);
738 }
739
740
741
742 /*
743 *----------------------------------------------------------------------
744 * NsfProfileRecordProcData --
745 *
746 * This function is invoked, when a call of a nsf::proc. It records
747 * time spent and count per nsf::proc.
748 *
749 * Results:
750 * None
751 *
752 * Side effects:
753 * Updated or created profile data entries
754 *
755 *----------------------------------------------------------------------
756 */
757 void
NsfProfileRecordProcData(Tcl_Interp * interp,const char * methodName,long startSec,long startUsec)758 NsfProfileRecordProcData(Tcl_Interp *interp, const char *methodName, long startSec, long startUsec) {
759 NsfRuntimeState *rst;
760 NsfProfile *profilePtr;
761 double totalMicroSec;
762 struct Tcl_Time trt;
763
764 nonnull_assert(interp != NULL);
765 nonnull_assert(methodName != NULL);
766
767 rst = RUNTIME_STATE(interp);
768 profilePtr = &rst->profile;
769
770 Tcl_GetTime(&trt);
771
772 totalMicroSec = (double)((trt.sec - startSec) * 1000000 + (trt.usec - startUsec));
773 profilePtr->overallTime += (long)totalMicroSec;
774
775 if (rst->doTrace) {
776 NsfProfileTraceExitAppend(interp, methodName, totalMicroSec);
777 }
778
779 NsfProfileFillTable(&profilePtr->procData, methodName, totalMicroSec);
780 }
781
782 /*
783 *----------------------------------------------------------------------
784 * NsfProfileClearTable --
785 *
786 * Clear all data in a profile table.
787 *
788 * Results:
789 * None
790 *
791 * Side effects:
792 * freed profile information.
793 *
794 *----------------------------------------------------------------------
795 */
796
797
798 static void NsfProfileClearTable(Tcl_HashTable *table) nonnull(1);
799
800 static void
NsfProfileClearTable(Tcl_HashTable * table)801 NsfProfileClearTable(Tcl_HashTable *table) {
802 Tcl_HashSearch hSrch;
803 Tcl_HashEntry *hPtr;
804
805 nonnull_assert(table != NULL);
806
807 for (hPtr = Tcl_FirstHashEntry(table, &hSrch); hPtr != NULL;
808 hPtr = Tcl_NextHashEntry(&hSrch)) {
809 NsfProfileData *value = (NsfProfileData *) Tcl_GetHashValue(hPtr);
810 ckfree((char *) value);
811 Tcl_DeleteHashEntry(hPtr);
812 }
813 }
814
815 /*
816 *----------------------------------------------------------------------
817 * NsfProfileClearData --
818 *
819 * Flush all data in all profile tables and reset the time
820 * counters.
821 *
822 * Results:
823 * None
824 *
825 * Side effects:
826 * freed profile information.
827 *
828 *----------------------------------------------------------------------
829 */
830
831 void
NsfProfileClearData(Tcl_Interp * interp)832 NsfProfileClearData(Tcl_Interp *interp) {
833 NsfProfile *profilePtr;
834 struct Tcl_Time trt;
835
836 nonnull_assert(interp != NULL);
837
838 profilePtr = &RUNTIME_STATE(interp)->profile;
839 NsfProfileClearTable(&profilePtr->objectData);
840 NsfProfileClearTable(&profilePtr->methodData);
841 NsfProfileClearTable(&profilePtr->procData);
842
843 Tcl_GetTime(&trt);
844 profilePtr->startSec = trt.sec;
845 profilePtr->startUSec = trt.usec;
846 profilePtr->overallTime = 0;
847 profilePtr->depth = 0;
848
849 Tcl_DStringSetLength(&profilePtr->traceDs, 0);
850 }
851
852 /*
853 *----------------------------------------------------------------------
854 * NsfProfileGetTable --
855 *
856 * Return the profiling information for the specified profile table
857 * in form of a Tcl list.
858 *
859 * Results:
860 * Tcl List
861 *
862 * Side effects:
863 * None.
864 *
865 *----------------------------------------------------------------------
866 */
867 static Tcl_Obj* NsfProfileGetTable(Tcl_Interp *interp, Tcl_HashTable *table) nonnull(1) nonnull(2);
868
869 static Tcl_Obj*
NsfProfileGetTable(Tcl_Interp * interp,Tcl_HashTable * table)870 NsfProfileGetTable(Tcl_Interp *interp, Tcl_HashTable *table) {
871 Tcl_Obj *list = Tcl_NewListObj(0, NULL);
872 Tcl_HashSearch hSrch;
873 Tcl_HashEntry *hPtr;
874
875 nonnull_assert(interp != NULL);
876 nonnull_assert(table != NULL);
877
878 for (hPtr = Tcl_FirstHashEntry(table, &hSrch); hPtr != NULL;
879 hPtr = Tcl_NextHashEntry(&hSrch)) {
880 NsfProfileData *value = (NsfProfileData *) Tcl_GetHashValue(hPtr);
881 char *key = Tcl_GetHashKey(table, hPtr);
882 Tcl_Obj *subList = Tcl_NewListObj(0, NULL);
883
884 Tcl_ListObjAppendElement(interp, subList, Tcl_NewStringObj(key, -1));
885 Tcl_ListObjAppendElement(interp, subList, Tcl_NewLongObj(value->microSec));
886 Tcl_ListObjAppendElement(interp, subList, Tcl_NewLongObj(value->count));
887 Tcl_ListObjAppendElement(interp, list, subList);
888 }
889 return list;
890 }
891
892 /*
893 *----------------------------------------------------------------------
894 * NsfProfileGetData --
895 *
896 * Return recorded profiling information. This function returns a
897 * list containing (a) the elapsed time since the last clear (or
898 * init), (b) the cumulative time, (c) the list with the per-object
899 * data and (d) the list with the method invocation data.
900 *
901 * Results:
902 * Tcl List
903 *
904 * Side effects:
905 * None.
906 *
907 *----------------------------------------------------------------------
908 */
909
910 void
NsfProfileGetData(Tcl_Interp * interp)911 NsfProfileGetData(Tcl_Interp *interp) {
912 Tcl_Obj *list = Tcl_NewListObj(0, NULL);
913 NsfProfile *profilePtr;
914 long totalMicroSec;
915 struct Tcl_Time trt;
916
917 nonnull_assert(interp != NULL);
918
919 profilePtr = &RUNTIME_STATE(interp)->profile;
920 Tcl_GetTime(&trt);
921 totalMicroSec = (trt.sec - profilePtr->startSec) * 1000000 + (trt.usec - profilePtr->startUSec);
922
923 Tcl_ListObjAppendElement(interp, list, Tcl_NewLongObj((long)totalMicroSec));
924 Tcl_ListObjAppendElement(interp, list, Tcl_NewLongObj(profilePtr->overallTime));
925 Tcl_ListObjAppendElement(interp, list, NsfProfileGetTable(interp, &profilePtr->objectData));
926 Tcl_ListObjAppendElement(interp, list, NsfProfileGetTable(interp, &profilePtr->methodData));
927 Tcl_ListObjAppendElement(interp, list, NsfProfileGetTable(interp, &profilePtr->procData));
928 Tcl_ListObjAppendElement(interp, list, Tcl_NewStringObj(profilePtr->traceDs.string, profilePtr->traceDs.length));
929
930 Tcl_SetObjResult(interp, list);
931 }
932
933 /*
934 *----------------------------------------------------------------------
935 * NsfProfileInit --
936 *
937 * Initialize the profiling information. This is a one-time only
938 * operation and initializes the hash table and the timing
939 * results. The inverse operation is NsfProfileFree()
940 *
941 * Results:
942 * None.
943 *
944 * Side effects:
945 * None.
946 *
947 *----------------------------------------------------------------------
948 */
949 void
NsfProfileInit(Tcl_Interp * interp)950 NsfProfileInit(Tcl_Interp *interp) {
951 NsfProfile *profilePtr;
952 struct Tcl_Time trt;
953
954 nonnull_assert(interp != NULL);
955
956 profilePtr = &RUNTIME_STATE(interp)->profile;
957 Tcl_InitHashTable(&profilePtr->objectData, TCL_STRING_KEYS);
958 Tcl_InitHashTable(&profilePtr->methodData, TCL_STRING_KEYS);
959 Tcl_InitHashTable(&profilePtr->procData, TCL_STRING_KEYS);
960
961 Tcl_GetTime(&trt);
962 profilePtr->startSec = trt.sec;
963 profilePtr->startUSec = trt.usec;
964 profilePtr->overallTime = 0;
965 profilePtr->depth = 0;
966 Tcl_DStringInit(&profilePtr->traceDs);
967 }
968
969 /*
970 *----------------------------------------------------------------------
971 * NsfProfileFree --
972 *
973 * Free all profiling information. This is a one-time only
974 * operation only. The inverse operation is NsfProfileInit().
975 *
976 * Results:
977 * None.
978 *
979 * Side effects:
980 * None.
981 *
982 *----------------------------------------------------------------------
983 */
984 void
NsfProfileFree(Tcl_Interp * interp)985 NsfProfileFree(Tcl_Interp *interp) {
986 NsfProfile *profilePtr;
987
988 nonnull_assert(interp != NULL);
989
990 profilePtr = &RUNTIME_STATE(interp)->profile;
991 NsfProfileClearData(interp);
992 Tcl_DeleteHashTable(&profilePtr->objectData);
993 Tcl_DeleteHashTable(&profilePtr->methodData);
994 Tcl_DeleteHashTable(&profilePtr->procData);
995 Tcl_DStringFree(&profilePtr->traceDs);
996 }
997 #endif
998
999 /*
1000 * Local Variables:
1001 * mode: c
1002 * c-basic-offset: 2
1003 * fill-column: 78
1004 * indent-tabs-mode: nil
1005 * End:
1006 */
1007