wvLib.c
上传用户:nvosite88
上传日期:2007-01-17
资源大小:4983k
文件大小:82k
源码类别:

VxWorks

开发平台:

C/C++

  1. /* wvLib.c - event logging control library (WindView) */
  2. /* Copyright 1994-2001 Wind River Systems, Inc. */
  3. /*
  4. modification history
  5. --------------------
  6. 02x,26oct01,tcr  fix problem with half-word alignment in wvLogHeaderCreate()
  7. 02w,24oct01,tcr  Fix comment string for Diab compiler
  8. 02v,18oct01,tcr  move to VxWorks 5.5, add VxWorks Events
  9. 02u,03mar99,dgp  update to reference project facility instead of config.h
  10. 02t,31aug98,cth  FCS man page edit
  11. 02s,28aug98,dgp  FCS man page edit, change order of sections
  12. 02r,18aug98,cjtc event buffer full handled more gracefully (SPR 22133)
  13. 02q,06aug98,cth  added retry to upload path write on EWOULDBLOCK, updated docs
  14. 02p,16jul98,cjtc wvUploadStop returns status of upload task (SPR 21461)
  15. 02o,09jul98,cjtc fix problems with task name preservation (SPR 21350)
  16. 02n,26may98,dgp  final doc editing for WV 2.0
  17. 02m,14may98,cth  added lib man page, other doc changes
  18. 02l,13may98,cth  added continued upload if thresh bytes still in buffer, 
  19.  added ints locked on buffer access, added wvUploadTaskConfig
  20. 02k,07may98,dgp  clean up man pages for WV 2.0 beta release
  21. 02j,20apr98,cth  added wvLogHeaderCreate/Upload, changed wvEvtLogInit, cleanup
  22.  removed separate cont/defer upload priorities
  23. 02i,17apr98,cth  reworked event-log header uploading and storage, cleaned up,
  24.  added status to wvUpTaskId
  25. 02h,15apr98,cth  added tnHashTbl functions for post-mortem taskname preserving,
  26.  added wvTaskNameBufAdd, wvTaskNamesPreserve, wvTaskNamesUpload
  27. 02g,17feb98,pr   added memLib support for object instrumentation
  28. 02f,27jan98,cth  removed evtLogHeaderCreate, changed wvLibInit/2, added
  29.  wvEvtLogInit, changed upTask priority to 150, doc changes
  30. 02e,23jan98,pr   modified WV macros, deleted trgCheck binding.
  31. 02d,18dec97,cth  updated includes, removed tmp references, wvOn/Off, and
  32.  rBuffErrorHandler
  33. 02c,14dec97,pr   deleted some windview 1.0 variables.
  34. 02b,16nov97,cth  reworked for wv2.0
  35. 02a,26aug97,pr   modified evtLog functions initialization and added trgCheck.
  36.             cth  added wvEvtLogDisable in the event of a buffer overflow.
  37. 01z,18aug97,pr   added temporary function pointer initializer for 
  38.  CTX level.
  39. 01y,11aug97,nps  fixed typo causing compilation problem.
  40. 01x,29jul97,nps  added ring buffer support.
  41. 01w,24jun97,pr   replaced evtLogTIsOn with evtInstMode
  42.  Added return (ERROR) for default case in wvEvtLogEnable
  43. 01v,09aug96,pr   deleted obj instrumentation functions from wvEvtLogEnable
  44. 01u,07aug96,pr   Added object instrumentation functions to wvEvtLog[Enable
  45.                  ,Disable] for SPR #6998.
  46. 01t,11jul96,pr   added ifdef for PPC function evtLogT1_noTS
  47. 01s,08jul96,pr   added evtLogT1_noTS
  48. 01r,03feb95,rhp  doc tweaks from last printed version of man pages
  49. 01q,01feb95,rhp  lib man page: add SEE ALSO ref to User's Guide
  50. 01p,15sep94rdc   wvEvent insures logging is enabled to minimize overhead
  51.  when logging is turned off.
  52. 01o,20oct94,rdc  increased evtTask stack size.
  53. 01n,14apr94,smb  Changes made to wvObjInst to treat task objects and other
  54.  system object instrumentation differently.
  55. 01m,05apr94,smb  documentation modifications.
  56. 01l,30mar94,smb  fixed event buffer overflow race.
  57. 01k,07mar94,smb  changed prototypes for OSE functionality
  58.  removed PASSIVE_MODE
  59.  documentation modifications
  60. 01j,22feb94,smb  changed typedef EVENT_TYPE to event_t (SPR #3064)
  61. 01i,15feb94,smb  renamed collection modes for wvInstInit SPR #3049
  62.  parameter checking SPR #3050
  63.  added scratch-pad reset.
  64. 01h,01feb94,smb  fixed return values for wvEvtLog[Enable,Disable] SPR #2994 
  65.  object are not instrumented until specified SPR #2985
  66. 01g,24jan94,smb  added INT_RESTRICT to wvEvtLogEnable and wvEvtLogDisable
  67.  initialised function pointer for portable kernel
  68. 01f,19jan94,smb  documentation changes
  69.  wvEvtLog changes to wvEvtLogEnable and wvEvtLogDisable
  70.  tEvtTask() is no longer spawned in POST_MODE
  71.  added wvEvtLogStop(), wvOn() and wvOff routines
  72. 01e,18jan94,maf  tEvtTask() no longer calls connectionClose() upon event
  73.    buffer overflow (part of fix for SPR #2800).
  74.  tEvtTask() now uploads last buffer upon event buffer
  75.    overflow (SPR #2888).
  76. 01d,13jan94,c_s  adjusted documentation for wvEvent () and removed NOMANUAL.
  77.                    (SPR #2870).  
  78. 01c,04jan94,c_s  wvInstInit doesn't attempt to free user-supplied memory 
  79.    (SPR #2750).
  80. 01b,15dec93,smb  documentation additions
  81.  allows wvEvtLog() to be called multiple times.
  82. 01a,10dec93,smb  created
  83. */
  84. /*
  85. DESCRIPTION
  86. This library contains routines that control event collection and upload of
  87. event data from the target to various destinations.  The routines define
  88. the interface for the target component of WindView.  When event data has
  89. been collected, the routines in this library are used to produce event
  90. logs that can be understood by the WindView host tools.
  91. An event log is made up of a header, followed by the task names of each
  92. task present in the system when the log is started, followed by a string of
  93. events produced by the various event points throughout the kernel and
  94. associated libraries.  In general, this information is gathered and stored
  95. temporarily on the target, and later uploaded to the host in the proper
  96. order to form an event log.  The routines in this file can be used to
  97. create logs in various ways, depending on which routines are called, and in
  98. which order the routines are called.  
  99. There are three methods for uploading event logs.  The first is to defer
  100. upload of event data until after logging has been stopped in order to
  101. eliminate events associated with upload activity from the event log.  The
  102. second is to continuously upload event data as it is gathered.  This
  103. allows the collection of very large event logs, that may contain more
  104. events than the target event buffer can store at one time.  The third is
  105. to defer upload of the data until after a target reboot.  This
  106. method allows event data to continuously overwrite earlier data in the
  107. event buffer, creating a log of the events leading to a target failure (a
  108. post-mortem event log).
  109. Each of these three methods is explained in more detail 
  110. in CREATING AN EVENT LOG.
  111. EVENT BUFFERS AND UPLOAD PATHS
  112. Many of the routines in wvLib require access to the buffer used to store event 
  113. data (the event buffer) and to the communication paths from the target to the 
  114. host (the upload paths).  Both the buffer and the path are referenced with IDs 
  115. that provide wvLib with the appropriate information for access.  
  116. The event buffering mechanism used by wvLib is provided by rBuffLib.
  117. The upload paths available for use with wvLib are provided by
  118. wvFileUploadPathLib, wvTsfsUploadPathLib and wvSockUploadPathLib.
  119. The upload mechanism backs off and retries writing to the upload path
  120. if an error occurs during the write attempt with the errno 'EAGAIN' 
  121. or 'EWOULDBLOCK'.  Two global variables are used to set the amount of time to
  122. back off and the number of retries.  The variables are:
  123. .CS
  124.     int wvUploadMaxAttempts   /@ number of attempts to try writing @/
  125.     int wvUploadRetryBackoff  /@ delay between tries (in ticks - 60/sec) @/
  126. .CE
  127. INITIALIZATION
  128. This library is initialized in two steps.  The first step, done by calling
  129. wvLibInit(), associates event logging routines to system objects.  This is
  130. done when the kernel is initialized.  The second step, done by calling
  131. wvLibInit2(), associates all other event logging routines with the
  132. appropriate event points.  Initialization is done automatically when
  133. INCLUDE_WINDVIEW is defined.
  134. Before event logging can be started, and each time a new event buffer is
  135. used to store logged events, wvEvtLogInit() must be called to bind the
  136. event logging routines to a specific buffer.
  137. DETERMINING WHICH EVENTS ARE COLLECTED
  138. There are three classes of events that can be collected.  They are:
  139. .CS
  140.     WV_CLASS_1              /@ Events causing context switches @/
  141.     WV_CLASS_2             /@ Events causing task-state transitions @/
  142.     WV_CLASS_3             /@ Events from object and system libraries @/
  143. .CE
  144. The second class includes all of the events contained within the first
  145. class, plus additional events causing task-state transitions but not
  146. causing context switches.  The third class contains all of the second, and
  147. allows logging of events within system libraries.  It can also be
  148. limited to specific objects or groups of objects:
  149. .iP 
  150. Using wvObjInst() allows individual objects (for example, 'sem1') to be 
  151. instrumented.
  152. .iP
  153. Using wvSigInst() allows signals to be instrumented.
  154. .iP
  155. Using wvObjInstModeSet() allows finer control over what type of objects are
  156. instrumented.  wvObjInstModeSet() allows types of system objects (for example,
  157. semaphores, watchdogs) to be instrumented as they are created.  
  158. .LP
  159. Logging events in Class 3 generates the most data, which may be helpful
  160. during analysis of the log.  It is also the most intrusive on the system,
  161. and may affect timing and performance.  Class 2 is more intrusive than
  162. Class 1.  In general, it is best to use the lowest class that still 
  163. provides the required level of detail.
  164. To manipulate the class of events being logged, the following routines can
  165. be used:  wvEvtClassSet(), wvEvtClassGet(), wvEvtClassClear(), and
  166. wvEvtClassClearAll().  To log a user-defined event, wvEvent() can be used.  
  167. It is also possible to log an event from any point during execution using e(), 
  168. located in dbgLib.
  169. CONTROLLING EVENT LOGGING
  170. Once the class of events has been specified, event logging can be started
  171. with wvEvtLogStart() and stopped with wvEvtLogStop().
  172. CREATING AN EVENT LOG
  173. An event log consists of a header, a section of task names, and a list of
  174. events logged after calling wvEvtLogStart().  As discussed above, there are
  175. three common ways to upload an event log.
  176. .SS "Deferred Upload"
  177. When creating an event log by uploading the event data after event logging
  178. has been stopped (deferred upload), the following series of calls can be
  179. used to start and stop the collection.  In this example the memory
  180. allocated to store the log header is in the system partition.  The event
  181. buffer should be allocated from the system memory partition as well. 
  182. Error checking has been eliminated to simplify the example.
  183. .CS
  184.     /@ wvLib and rBuffLib initialized at system start up @/  
  185.     #include "vxWorks.h"
  186.     #include "wvLib.h"
  187.     #include "private/wvBufferP.h"
  188.     #include "private/wvUploadPathP.h"
  189.     #include "private/wvFileUploadPathLibP.h"
  190.     BUFFER_ID   bufId;
  191.     UPLOAD_ID pathId;
  192.     WV_UPLOAD_TASK_ID upTaskId;
  193.     WV_LOG_HEADER_ID hdrId;
  194.     /@ 
  195.      @ To prepare the event log and start logging: 
  196.      @/
  197.     /@ Create event buffer in memSysPart, yielding bufId. @/
  198.     wvEvtLogInit (bufId);
  199.     hdrId = wvLogHeaderCreate (memSysPartId);
  200.     wvEvtClassSet (WV_CLASS_1); /@ set to log class 1 events @/
  201.     wvEvtLogStart ();
  202.     /@ 
  203.      @ To stop logging and complete the event log. 
  204.      @/
  205.     wvEvtLogStop ();
  206.     /@ Create an uplaod path using wvFileUploadPathLib, yielding pathId. @/
  207.     wvLogHeaderUpload (hdrId, pathId);
  208.     upTaskId = wvUploadStart (bufId, pathId, TRUE);
  209.     wvUploadStop (upTaskId);
  210.     /@ Close the upload path and destroy the event buffer @/
  211. .CE
  212. Routines which can be used as they are, or modified to meet the users needs,
  213. are located in usrWindview.c.  These routines, wvOn() and wvOff(), provide a
  214. way to produce useful event logs without using the host user interface of
  215. WindView.
  216. .SS "Continuous Upload"
  217. When uploading event data as it is still being logged to the event buffer
  218. (continuous upload), simply rearrange the above calls:
  219. .CS
  220.     /@ Includes and declarations. @/
  221.     /@ 
  222.      @ To prepare the event log and start logging: 
  223.      @/
  224.     /@ Create event buffer in memSysPart, yielding bufId. @/
  225.     /@ Create an uplaod path, yielding pathId. @/
  226.     wvEvtLogInit (bufId);
  227.     upTaskId = wvUploadStart (bufId, pathId, TRUE);
  228.     hdrId = wvLogHeaderCreate (memSysPartId);
  229.     wvLogHeaderUpload (hdrId, pathId);
  230.     wvEvtClassSet (WV_CLASS_1); /@ set to log class 1 events @/
  231.     wvEvtLogStart ();
  232.     /@ 
  233.      @ To stop logging and complete the event log: 
  234.      @/
  235.     wvEvtLogStop ();
  236.     wvUploadStop (upTaskId);
  237.     /@ Close the upload path and destroy the event buffer @/
  238. .CE
  239. .SS "Post-Mortem Event Collection"
  240. This library also contains routines that preserve task name information
  241. throughout event logging in order to produce post-mortem event logs:
  242. wvTaskNamesPreserve() and wvTaskNamesUpload().
  243. Post-mortem event logs typically contain events leading up to a target
  244. failure.  The memory containing the information to be stored in the log
  245. must not be zeroed when the system reboots.  The event buffer is set up to
  246. allow event data to be logged to it continuously, overwriting the data
  247. collected earlier.  When event logging is stopped, either by a system
  248. failure or at the request of the user, the event buffer may not contain
  249. the first events logged due to the overwriting.  As tasks are created the
  250. EVENT_TASKNAME that is used by the WindView host tools to associate a task
  251. ID with a task name can be overwritten, while other events pertaining to
  252. that task ID may still be present in the event buffer.  In order to assure
  253. that the WindView host tools can assign a task name to a context, a copy
  254. of all task name events can be preserved outside the event buffer and
  255. uploaded separately from the event buffer.
  256. Note that several of the routines in wvLib, including
  257. wvTaskNamesPreserve(), take a memory partition ID as an argument.  This
  258. allows memory to be allocated from a user-specified partition.  For
  259. post-mortem data collection, the memory partition should be within memory
  260. that is not zeroed upon system reboot.  The event buffer, preserved task
  261. names, and log header should be stored in this partition.
  262. Generating a post-mortem event log is similar to generating a deferred
  263. upload log.  Typically event logging is stopped due to a system failure,
  264. but it may be stopped in any way.  To retrieve the log header, task name
  265. buffer, and event buffer after a target reboot, these IDs must be remembered
  266. or stored along with the collected information in the non-zeroed memory.
  267. Also, the event buffer should be set to allow continuous logging by
  268. overwriting earlier event data.  The following produces a post-mortem log.
  269. The non-zeroed memory partition has the ID <postMortemPartId>.
  270. .CS
  271.     /@ Includes, as in the examples above. @/
  272.     BUFFER_ID   bufId;
  273.     UPLOAD_ID pathId;
  274.     WV_UPLOAD_TASK_ID upTaskId;
  275.     WV_LOG_HEADER_ID hdrId;
  276.     WV_TASKBUF_ID taskBufId;
  277.     /@ 
  278.      @ To prepare the event log and start logging: 
  279.      @/
  280.     /@ 
  281.      @ Create event buffer in non-zeroed memory, allowing overwrite, 
  282.      @ yielding bufId. 
  283.      @/
  284.     wvEvtLogInit (bufId);
  285.     taskBufId = wvTaskNamesPreserve (postMortemPartId, 32);
  286.     hdrId = wvLogHeaderCreate (postMortemPartId);
  287.     wvEvtClassSet (WV_CLASS_1); /@ set to log class 1 events @/
  288.     wvEvtLogStart ();
  289.     /@ 
  290.      @ System fails and reboots.  Note that taskBufId, bufId and
  291.      @ hdrId must be preserved through the reboot so they can be
  292.      @ used to upload the data.
  293.      @/
  294.     /@ Create an uplaod path, yielding pathId. @/
  295.     wvLogHeaderUpload (hdrId, pathId);
  296.     upTaskId = wvUploadStart (bufId, pathId, TRUE);
  297.     wvUploadStop (upTaskId);
  298.     wvTaskNamesUpload (taskBufId, pathId);
  299.     /@ Close the upload path and destroy the event buffer @/
  300. .CE
  301. INCLUDE FILES: wvLib.h eventP.h
  302. SEE ALSO: rBuffLib, wvFileUploadPathLib, wvSockUploadPathLib,
  303. wvTsfsUploadPathLib,
  304. .I WindView User's Guide
  305. INTERNAL
  306. All buffer accesses are made with interrupts locked.
  307. Event logging is started by setting the evtAction variable, which is done
  308. by the WV_ACTION_SET macro.  This global variable acts as a guard around
  309. all event points.  When the first byte of this 16-bit word is TRUE, the
  310. windview event point will then check the global wvEvtClass to dertermine
  311. if this event point belongs to any of the classes being logged.  The
  312. second half of evtAction is used in the same way for triggering.  The
  313. event points that should respond when evtAction's triggering half is TRUE
  314. are determined by the trgEvtClass variable.
  315. In the most common case, when neither windview nor triggering is being
  316. used within the kernel, having two levels of variables (evtAction is the
  317. first level, wvEvtClass and trgEvtClass are the second) reduces the work
  318. at each event point to the testing of a single variable, evtAction.
  319. Unfortunately, when either windview or triggering is on, three tests,
  320. rather than just two, have to be performed.
  321. All three global variables, evtAction, wvEvtClass trgEvtClass are managed
  322. through the macro interface defined in eventP.h.  The variables themselves
  323. never need to be referenced explicitly.
  324. The event buffer and upload paths provide a fairly simple interface 
  325. that allows wvLib to use them.  Other buffers and upload paths can be
  326. easily added by follwing the same interface.  
  327. The buffer ID passed to many of wvLib's routines is actually a pointer to
  328. a structure defined in wvBufferP.h.  This structure contains function 
  329. pointers for reading and writing, and communicates threshold information from
  330. the buffering library.  evtLogLib also uses this ID to write to the buffers.
  331. The buffer library usually considers the ID as a pointer to a structure 
  332. whose first members are like the structure defined in wvBufferP.h, but
  333. whose other members are used to store its own private information.
  334. Upload paths are accessed in a similar way to the event buffers,  but are 
  335. much simpler.  The structure that defines their interface is defined in
  336. wvUploadPathP.h.
  337. A hash table is implemented within wvLib to maintain task name events for
  338. post mortem log generation, as described above.  This was done because it
  339. is possible for a tid to be reused, leaving more than one taskname for a 
  340. single tid.  In this case one of the names has to be chosen, and in our
  341. case it is the last.  The hash table provides near constant time lookup of
  342. events as they are logged, in order to ensure that there is only ever one
  343. name per tid in the table at any time.  The hash tables were rewritten here,
  344. rather than using the vxWorks' hashLib, because the storage has to 
  345. relocatable to other memory partitions than the system partition.  
  346. This hash table is complete overkill however, because the WindView parser
  347. can accept any number of taskname events with a single tid.  It always 
  348. updates the name to the last name it received for a tid.  Given this, it
  349. is not necessary to ensure there are no duplicate names per tid in the
  350. taskname buffer.  With no lookup facility necessary, a simple linked list
  351. would suffice for this data structure.  Well, the hash table works.
  352. */
  353. #include "vxWorks.h"
  354. #include "intLib.h"
  355. #include "logLib.h"
  356. #include "msgQLib.h"
  357. #include "semLib.h"
  358. #include "private/memPartLibP.h"
  359. #include "string.h"
  360. #include "taskLib.h"
  361. #include "wdLib.h"
  362. #include "sysLib.h"
  363. #include "errnoLib.h"
  364. #include "wvLib.h"
  365. #include "private/eventP.h"
  366. #include "private/objLibP.h"
  367. #include "private/sigLibP.h"
  368. #include "private/eventLibP.h"
  369. #include "private/taskLibP.h"
  370. #include "private/kernelLibP.h"
  371. #include "private/workQLibP.h"
  372. #include "private/semLibP.h"
  373. #include "private/evtLogLibP.h"
  374. #include "private/wvBufferP.h"
  375. #include "private/wvUploadPathP.h"
  376. #include "private/wvLibP.h"
  377. /* global */
  378. int wvUploadTaskPriority      = 150;         /* upload task priority */
  379. int wvUploadTaskStackSize     = 5000;         /* upload task stack size */
  380. int wvUploadTaskOptions       = VX_UNBREAKABLE; /* upload task options */
  381. int wvUploadMaxAttempts       = 200;            /* upload write retries */
  382. int wvUploadRetryBackoff      = 6;              /* upload retry delay - ticks */
  383. PART_ID pmPartId ; /* post mortem partition id */
  384. BOOL    wvEvtBufferFullNotify = FALSE; /* notify the host that the evt buff */
  385. /* is full. Will be reset by the */
  386. /* when seen (handshake) or when */
  387. /* wvEvtLogStart is called */
  388. /* local */
  389. static BUFFER_ID    wvEvtBufferId; /* a local copy for access by the host
  390.    through wvEvtBufferGet */
  391. static TN_HASH_TBL *tnHashTbl; /* Local copy of post-mortem taskname
  392.    event hash table.  This is required
  393.    for adding events, because events 
  394.    are added directly from event points
  395.    and this info isn't known at the
  396.    event point. */
  397. /* extern */
  398. extern CLASS_ID msgQClassId;
  399. extern CLASS_ID wdClassId;
  400. /* forward declarations */
  401. static STATUS       uploadPathWrite        (UPLOAD_ID pathId, char *pData,
  402.     int nBytes);
  403. static STATUS      wvUpload    (WV_UPLOADTASK_ID upTaskId);
  404. static TASKBUF_ID   tnHashTblCreate        (PART_ID memPart, int tblSize);
  405. static STATUS       tnHashTblDestroy       (TN_HASH_TBL *pTbl);
  406. static int      tnHash             (TN_HASH_TBL *tbl, int key);
  407. static TN_ITER_KEY *tnHashTblIterInit      (TN_HASH_TBL *pTbl);
  408. static void         tnHashTblIterDone      (TN_ITER_KEY *pIterKey, 
  409.     TN_HASH_TBL *pTbl);
  410. static TN_NODE     *tnHashTblIterNext      (TN_HASH_TBL *pTbl, 
  411.     TN_ITER_KEY *pIterKey);
  412. static TN_EVENT    *tnHashTblIterNextEvent (TN_HASH_TBL *pTbl, 
  413.     TN_ITER_KEY *pIterKey);
  414. static STATUS       tnHashTblInsert        (TN_HASH_TBL *pTbl, 
  415.     TN_EVENT *pEvent, int key, 
  416.     TN_EVENT *pReplacedEvent);
  417. /*******************************************************************************
  418. *
  419. * wvLibInit - initialize wvLib - first step (WindView)
  420. *
  421. * This routine starts initializing wvLib.  Its actions should be performed 
  422. * before object creation, so it is called from usrKernelInit() in usrKernel.c.
  423. *
  424. * RETURNS: N/A
  425. */
  426. void wvLibInit (void)
  427.     {
  428.     evtObjLogFuncBind ();
  429.     }
  430. /*******************************************************************************
  431. *
  432. * wvLibInit2 - initialize wvLib - final step (WindView)
  433. *
  434. * This routine is called after wvLibInit() to complete the initialization of
  435. * wvLib.  It should be called before starting any event logging.
  436. *
  437. * RETURNS: N/A
  438. */
  439. void wvLibInit2 (void)
  440.     {
  441.     evtLogFuncBind ();
  442.     }
  443. /*******************************************************************************
  444. *
  445. * wvEvtLogInit - initialize an event log (WindView)
  446. *
  447. * This routine initializes event logging by associating a particular event
  448. * buffer with the logging functions.  It must be called before event logging
  449. * is turned on.
  450. *
  451. * RETURNS: N/A
  452. */
  453. void wvEvtLogInit 
  454.     (
  455.     BUFFER_ID  evtBufId /* event-buffer id */
  456.     )
  457.     {
  458.     /* Bind the buffer identified by evtBufId to the logging functions. */
  459.     evtBufferBind (evtBufId);
  460.     /* Stash a private copy of the id to return from wvEvtBufferGet. */
  461.     wvEvtBufferId = evtBufId;
  462.     }
  463. /*******************************************************************************
  464. * wvEvtLogStart - start logging events to the buffer (WindView)
  465. *
  466. * This routine starts event logging.  It also resets the timestamp
  467. * mechanism so that it can be called more than once without stopping event
  468. * logging.
  469. *
  470. * RETURNS: N/A
  471. */
  472. void wvEvtLogStart (void) 
  473.     {
  474.     /* 
  475.      * Initialize the time-stamp facility.  If event-logging is already
  476.      * enabled then we must disable the time-stamp facility before re-
  477.      * enabling it.
  478.      */
  479.     if (_func_tmrDisable != NULL && !WV_ACTION_IS_SET)
  480. (* _func_tmrDisable)();
  481.     if (_func_tmrEnable != NULL)
  482.         (* _func_tmrEnable) ();
  483.     /* Turn on event logging. */
  484.     wvEvtBufferFullNotify = FALSE;
  485.     WV_ACTION_SET;
  486.     }
  487. /*******************************************************************************
  488. *
  489. * wvEvtLogStop - stop logging events to the buffer (WindView)
  490. *
  491. * This routine turns off all event logging, including event-logging of
  492. * objects and signals specifically requested by the user.  In addition,
  493. * it disables the timestamp facility.
  494. *
  495. * RETURNS: N/A
  496. */
  497. void wvEvtLogStop (void)
  498.     {
  499.     /* Disable all event logging. */
  500.     WV_ACTION_UNSET;
  501.     /* Turn off any user-requested event logging within objects and signals. */
  502.     if (wvObjInstModeSet (INSTRUMENT_OFF) == INSTRUMENT_ON)
  503.         {
  504.         wvObjInst (OBJ_TASK, 0, INSTRUMENT_OFF);
  505.         wvObjInst (OBJ_SEM,  0, INSTRUMENT_OFF);
  506.         wvObjInst (OBJ_MSG,  0, INSTRUMENT_OFF);
  507.         wvObjInst (OBJ_WD,   0, INSTRUMENT_OFF);
  508.         wvObjInst (OBJ_MEM,  0, INSTRUMENT_OFF);
  509.         }
  510.     wvSigInst (INSTRUMENT_OFF);
  511.     wvEventInst (INSTRUMENT_OFF);
  512.     /* Disable the time-stamp facility. */
  513.     if (_func_tmrDisable != NULL)
  514.         (* _func_tmrDisable) ();
  515.     }
  516. /*******************************************************************************
  517. *
  518. * wvEvtClassSet - set the class of events to log (WindView)
  519. *
  520. * This routine sets the class of events which are logged when event
  521. * logging is started.  <classDescription> can take the following values:
  522. *
  523. * .CS
  524. *      WV_CLASS_1      /@ Events causing context switches @/
  525. *      WV_CLASS_2      /@ Events causing task-state transitions @/
  526. *      WV_CLASS_3      /@ Events from object and system libraries @/
  527. * .CE
  528. *
  529. * See wvLib for more information about these classes, particularly Class 3.
  530. *
  531. * RETURNS: N/A
  532. *
  533. * SEE ALSO:
  534. * wvObjInst(), wvObjInstModeSet(), wvSigInst(), wvEventInst()
  535. *
  536. */
  537. void wvEvtClassSet
  538.     (
  539.     UINT32 classDescription /* description of evt classes to set */
  540.     )
  541.     {
  542.     WV_EVTCLASS_SET (classDescription);
  543.     }
  544. /*******************************************************************************
  545. *
  546. * wvEvtClassGet - get the current set of classes being logged (WindView)
  547. *
  548. * This routine returns the set of classes currently being logged.
  549. *
  550. * RETURNS: The class description.
  551. */
  552. UINT32 wvEvtClassGet (void)
  553.     {
  554.     return (wvEvtClass);
  555.     }
  556. /*******************************************************************************
  557. *
  558. * wvEvtClassClear - clear the specified class of events from those being logged (WindView)
  559. *
  560. * This routine clears the class or classes described by <classDescription>
  561. * from the set of classes currently being logged.
  562. *
  563. * RETURNS: N/A
  564. */
  565. void wvEvtClassClear
  566.     (
  567.     UINT32 classDescription /* description of evt classes to clear */
  568.     )
  569.     {
  570.     WV_EVTCLASS_UNSET (classDescription);
  571.     }
  572. /*******************************************************************************
  573. *
  574. * wvEvtClassClearAll - clear all classes of events from those logged (WindView)
  575. *
  576. * This routine clears all classes of events so that no classes are logged
  577. * if event logging is started.
  578. *
  579. * RETURNS: N/A
  580. */
  581. void wvEvtClassClearAll (void)
  582.     {
  583.     WV_EVTCLASS_EMPTY;
  584.     }
  585. /*******************************************************************************
  586. *
  587. * wvObjInstModeSet - set object instrumentation on/off  (WindView)
  588. *
  589. * This routine causes objects to be created either instrumented or not
  590. * depending on the value of <mode>, which can be INSTRUMENT_ON or 
  591. * INSTRUMENT_OFF.  All objects created after wvObjInstModeSet() is called
  592. * with INSTRUMENT_ON and before it is called with INSTRUMENT_OFF are
  593. * created as instrumented objects.
  594. *
  595. * Use wvObjInst() if you want to enable instrumentation for a specific
  596. * object or set of objects.  Use wvSigInst() if you want to enable 
  597. * instrumentation for all signal activity, and wvEventInst() to 
  598. * enable instrumentation for VxWorks Event activity.
  599. *
  600. * This routine has effect only if INCLUDE_WINDVIEW is defined in
  601. * configAll.h.
  602. *
  603. * RETURNS: The previous value of <mode> or ERROR.
  604. *
  605. * SEE ALSO: wvObjInst(), wvSigInst(), wvEventInst()
  606. */
  607. STATUS wvObjInstModeSet 
  608.     (
  609.     int mode /* object instrumentation on/off */
  610.     )
  611.     {
  612.     if (mode == INSTRUMENT_ON) /* turn instrumentation on */
  613. {
  614. if (wvObjIsEnabled == TRUE)
  615.     return (INSTRUMENT_ON);
  616. wvObjIsEnabled = TRUE;
  617. return (INSTRUMENT_OFF);
  618. }
  619.     else if (mode == INSTRUMENT_OFF) /* turn instrumentation off */
  620.         {
  621.         if (wvObjIsEnabled == FALSE)
  622.             return (INSTRUMENT_OFF);
  623.         wvObjIsEnabled = FALSE;
  624.         return (INSTRUMENT_ON);
  625.         }
  626.     else
  627.         return (ERROR);
  628.     }
  629. /*******************************************************************************
  630. *
  631. * wvObjInst - instrument objects (WindView)
  632. *
  633. * This routine instruments a specified object or set of objects and has
  634. * effect when system objects have been enabled for event logging.
  635. *
  636. * <objType> can be set to one of the following: OBJ_TASK (tasks), OBJ_SEM 
  637. * (semaphores), OBJ_MSG (message queues), or OBJ_WD (watchdogs).
  638. * <objId> specifies the identifier of the particular object to be instrumented.
  639. * If <objId> is NULL, then all objects of <objType> have instrumentation
  640. * turned on or off depending on the value of <mode>. 
  641. *
  642. * If <mode> is INSTRUMENT_ON, instrumentation is turned on; if it is any 
  643. * other value (including INSTRUMENT_OFF) then instrumentation is turned off
  644. * for <objId>.
  645. *
  646. * Call wvObjInstModeSet() with INSTRUMENT_ON if you want to enable
  647. * instrumentation for all objects created after a certain place in your code.
  648. * Use wvSigInst() if you want to enable instrumentation for all signal activity.
  649. *
  650. * This routine has effect only if INCLUDE_WINDVIEW is defined in
  651. * configAll.h.
  652. *
  653. * RETURNS: OK or ERROR.
  654. *
  655. * SEE ALSO: wvSigInst(), wvEventInst(), wvObjInstModeSet()
  656. */
  657. STATUS wvObjInst
  658.     (
  659.     int    objType, /* object type */
  660.     void * objId, /* object ID or NULL for all objects */
  661.     int    mode  /* instrumentation mode */
  662.     )
  663.     {
  664.     if (objId == NULL) /* all objects of the same type */
  665.         {
  666.         if (mode == INSTRUMENT_ON)
  667.             {
  668.             /* Turn on instrumentation */
  669.             switch (objType)
  670.                 {
  671.                 case OBJ_TASK:
  672.                     taskClassId->instRtn = (FUNCPTR) evtLogOInt;
  673.                     break;
  674.                 case OBJ_SEM:
  675.                     semClassId->instRtn = (FUNCPTR) evtLogOInt;
  676.                     break;
  677.                 case OBJ_MSG:
  678.                     msgQClassId->instRtn = (FUNCPTR) evtLogOInt;
  679.                     break;
  680.                 case OBJ_WD:
  681.                     wdClassId->instRtn = (FUNCPTR) evtLogO;
  682.                     break;
  683.                 case OBJ_MEM:
  684.                     memPartClassId->instRtn = (FUNCPTR) evtLogOInt;
  685.                     break;
  686.                 default:
  687.                     return(ERROR);
  688.                 }
  689.             }
  690.         else
  691.             {
  692.     /* Turn off instrumentation */
  693.             switch (objType)
  694.                 {
  695.                 case OBJ_TASK:
  696.                     taskClassId->instRtn = (FUNCPTR) NULL;
  697.                     break;
  698.                 case OBJ_SEM:
  699.                     semClassId->instRtn = (FUNCPTR) NULL;
  700.                     break;
  701.                 case OBJ_MSG:
  702.                     msgQClassId->instRtn = (FUNCPTR) NULL;
  703.                     break;
  704.                 case OBJ_WD:
  705.                     wdClassId->instRtn = (FUNCPTR) NULL;
  706.                     break;
  707.                 case OBJ_MEM:
  708.                     memPartClassId->instRtn = (FUNCPTR) NULL;
  709.                     break;
  710.                 default:
  711.                     return(ERROR);
  712.                 }
  713.             }
  714. }
  715.     else /* per object basis */
  716.         {
  717.         /* verify the object */
  718. switch (objType)
  719.             {
  720.             case OBJ_TASK:
  721.                 {
  722.                 if (TASK_ID_VERIFY ((WIND_TCB *) objId) == ERROR)
  723.             return (ERROR);
  724.         break;
  725.         }
  726.             case OBJ_SEM:
  727.                 {
  728.                 if (OBJ_VERIFY ((SEM_ID) objId, semClassId) == ERROR)
  729.             return (ERROR);
  730. break;
  731. }
  732.             case OBJ_MSG:
  733.                 {
  734.                 if (OBJ_VERIFY ((MSG_Q_ID) objId, msgQClassId) == ERROR)
  735.     return (ERROR);
  736. break;
  737. }
  738.             case OBJ_WD:
  739.                 {
  740.                 if (OBJ_VERIFY ((WDOG_ID) objId, wdClassId) == ERROR)
  741.     return (ERROR);
  742. break;
  743. }
  744.            case OBJ_MEM:
  745.                 {
  746.                 if (OBJ_VERIFY ((PART_ID) objId, memPartClassId) == ERROR)
  747.             return (ERROR);
  748. break;
  749. }
  750.             default:
  751.                 return(ERROR);
  752.             }
  753.         /* the event logging routine is set/reset in the object's class */
  754.         if (mode == INSTRUMENT_ON)
  755.             {
  756.     /* change to instrumented class */
  757.             if (TASK_ID_VERIFY ((WIND_TCB *) objId) == ERROR)
  758.         {
  759.                 if (OBJ_EVT_RTN (objId) == (FUNCPTR) NULL)
  760.                     {
  761.             (((OBJ_ID)(objId))->pObjClass) =
  762.                         (struct obj_class *)
  763.                         (((OBJ_ID)(objId))->pObjClass->initRtn);
  764.                     }
  765. }
  766.     else
  767. {
  768.                 if (TASK_EVT_RTN (objId) == (FUNCPTR) NULL)
  769.                     {
  770.     ((OBJ_ID)(&((WIND_TCB *)(objId))->objCore))->pObjClass =
  771.         (struct obj_class *) (((OBJ_ID)
  772. (&((WIND_TCB *)(objId))->objCore))->pObjClass)->initRtn;
  773.       }
  774. }
  775.             }
  776.         else
  777.             {
  778.     /* change to non-instrumented class */
  779.     /* change to instrumented class */
  780.             if (TASK_ID_VERIFY ((WIND_TCB *) objId) == ERROR)
  781.         {
  782.                 if (OBJ_EVT_RTN (objId) != (FUNCPTR) NULL)
  783.                     {
  784.             (((OBJ_ID)(objId))->pObjClass) =
  785.                         (struct obj_class *)
  786.                         (((OBJ_ID)(objId))->pObjClass->initRtn);
  787.                     }
  788. }
  789.     else
  790.         {
  791.                 if (TASK_EVT_RTN (objId) != (FUNCPTR) NULL)
  792.                     {
  793.     ((OBJ_ID)(&((WIND_TCB *)(objId))->objCore))->pObjClass =
  794.                 (struct obj_class *) (((OBJ_ID)
  795. (&((WIND_TCB *)(objId))->objCore))->pObjClass)->initRtn;
  796.                     }
  797. }
  798.             }
  799.      return (OK);
  800.         }
  801.     return (ERROR); /* instrumentation is off */
  802.     }
  803. /*******************************************************************************
  804. *
  805. * wvSigInst - instrument signals (WindView)
  806. *
  807. * This routine instruments all signal activity.
  808. *
  809. * If <mode> is INSTRUMENT_ON, instrumentation for signals is turned on;
  810. * if it is any other value (including INSTRUMENT_OFF), instrumentation for
  811. * signals is turned off.
  812. *
  813. * This routine has effect only if INCLUDE_WINDVIEW is defined in
  814. * configAll.h and event logging has been enabled for system objects.
  815. *
  816. * INTERNAL
  817. * Because signals are not implemented as objects, they need their own logging
  818. * mechanism.
  819. *
  820. * RETURNS: OK or ERROR.
  821. */
  822. STATUS wvSigInst
  823.     (
  824.     int   mode  /* instrumentation mode */
  825.     )
  826.     {
  827.     if (mode == INSTRUMENT_ON)
  828.         {
  829. /* set signal event routine */
  830.         sigEvtRtn = (VOIDFUNCPTR) evtLogOInt;
  831.         }
  832.     else if (mode == INSTRUMENT_OFF)
  833.         {
  834.         /* reset signal routine */
  835.         sigEvtRtn = NULL;
  836.         }
  837.     else
  838.         return (ERROR);
  839.     return (OK);
  840.     }
  841. /*******************************************************************************
  842. *
  843. * wvEventInst - instrument VxWorks Events (WindView)
  844. *
  845. * This routine instruments VxWorks Event activity.
  846. *
  847. * If <mode> is INSTRUMENT_ON, instrumentation for VxWorks events is turned on;
  848. * if it is any other value (including INSTRUMENT_OFF), instrumentation for
  849. * VxWorks Events is turned off.
  850. *
  851. * This routine has effect only if INCLUDE_WINDVIEW is defined in
  852. * configAll.h and event logging has been enabled for system objects.
  853. *
  854. * INTERNAL
  855. * Because events are not implemented as objects, they need their own logging
  856. * mechanism.
  857. *
  858. * RETURNS: OK or ERROR.
  859. */
  860. STATUS wvEventInst
  861.     (
  862.     int   mode  /* instrumentation mode */
  863.     )
  864.     {
  865.     if (mode == INSTRUMENT_ON)
  866.         {
  867. /* set event logging routine */
  868.         eventEvtRtn = (VOIDFUNCPTR) evtLogOInt;
  869.         }
  870.     else if (mode == INSTRUMENT_OFF)
  871.         {
  872.         /* reset logging routine */
  873.         eventEvtRtn = NULL;
  874.         }
  875.     else
  876.         return (ERROR);
  877.     return (OK);
  878.     }
  879. /*******************************************************************************
  880. *
  881. * wvEvent - log a user-defined event (WindView)
  882. *
  883. * This routine logs a user event.  Event logging must have been started with
  884. * wvEvtLogEnable() or from the WindView GUI to use this routine.  
  885. * The <usrEventId> should be in the
  886. * range 0-25535.  A buffer of data can be associated with the event;
  887. * <buffer> is a pointer to the start of the data block, and <bufSize> is its
  888. * length in bytes.  The size of the event buffer configured with
  889. * wvInstInit() should be adjusted when logging large user events.
  890. * RETURNS: OK, or ERROR if the event can not be logged.
  891. *
  892. * SEE ALSO: dbgLib, e()
  893. *
  894. * INTERNAL
  895. * This event logging routine stores
  896. *       event id        (short )
  897. *       timestamp       (int)
  898. *       address         (int)   - always NULL
  899. *       buffer size     (int)   - if size is ZERO the buffer does not exist.
  900. *       buffer          (char)
  901. *
  902. */
  903. STATUS wvEvent
  904.     (
  905.     event_t    usrEventId,      /* event */
  906.     char *     buffer,          /* buffer */
  907.     size_t     bufSize          /* buffer size */
  908.     )
  909.     {
  910.     if (WV_ACTION_IS_SET)
  911. return (evtLogPoint (usrEventId, NULL, bufSize, buffer));
  912.     else
  913. return (OK);
  914.     }
  915. /*******************************************************************************
  916. *
  917. * uploadPathWrite - write data to upload path
  918. *
  919. * This routine attempts to write the data pointed to by pData to the uplaod
  920. * path indicated by pathId.  If the data can not be written due to a blocking
  921. * error such as EWOULDBLOCK or EAGAIN, this routine will delay for some time
  922. * using taskDelay(), and retry until the data is written.  If the data can
  923. * not be written within a reasonable number of attempts, or an error other
  924. * than EWOULDBLOCK or EAGAIN is generated while writing, this routine will
  925. * give up and return an error.
  926. *
  927. * RETURNS: OK, or ERROR if the data could not be written in a reasonable
  928. *          number of attempts, or if invalid pathId
  929. *
  930. * SEE ALSO:
  931. * NOMANUAL
  932. *
  933. */
  934. static STATUS uploadPathWrite
  935.     (
  936.     UPLOAD_ID     pathId,               /* path to write to */
  937.     char         *pData,                /* buffer of data to write */
  938.     int           nBytes                /* number bytes to write */
  939.     )
  940.     {
  941.     int   trys;                         /* to count the attempts */
  942.     int   nToWrite;                     /* number bytes left to write  */
  943.     int   nThisTry;                     /* number bytes written this try */
  944.     int   nWritten;                     /* total bytes written so far */
  945.     char *buf;                          /* local copy of pData */
  946.     trys     = 0;
  947.     nWritten = 0;
  948.     nThisTry = 0;
  949.     nToWrite = nBytes;
  950.     buf      = pData;
  951.     if (pathId == NULL || pathId->writeRtn == NULL)
  952.         return (ERROR);
  953.     while (nWritten < nBytes && trys < wvUploadMaxAttempts)
  954.         {
  955.         if ((nThisTry = pathId->writeRtn (pathId, buf, nToWrite)) < 0)
  956.             {
  957.             /* If write failed because it would have blocked, allow retry. */
  958.             if (errno == EWOULDBLOCK || errno == EAGAIN)
  959.                 taskDelay (wvUploadRetryBackoff);
  960.             else
  961.                 return (ERROR);
  962.             }
  963.         else
  964.             {
  965.             nWritten += nThisTry;
  966.             nToWrite -= nThisTry;
  967.             buf      += nThisTry;
  968.             }
  969.         ++trys;
  970.         }
  971.     /* Check if write failed to complete within the max number of tries. */
  972.     if (nWritten < nBytes)
  973.         return (ERROR);
  974.     return (OK);
  975.     }
  976. /*******************************************************************************
  977. *
  978. * wvUpload - transfer events to the host (WindView)
  979. *
  980. * This routine is used to upload events stored in the event buffer to the
  981. * host.  Events are read from the buffer using the routines and information
  982. * referenced by the BUFFER_ID.  The upload path is accessed using
  983. * routines referenced by the UPLOAD_ID.  Information useful for making this
  984. * routine exit when desired, and communicating when it has exited is 
  985. * contained in the WV_UPLOADTASK_ID.
  986. *
  987. * When exitWhenEmpty, within the WV_UPLOADTASK_ID, is set to true, wvUpload
  988. * will upload all available events, and then return immediately. If this
  989. * value is set to false, wvUpload will never exit, but it will block on the
  990. * buffer's semaphore until sufficient data becomes available to upload.
  991. * Setting exitWhenEmpty to false is used to continuously upload events as
  992. * they are logged to the buffer.  The semaphore uploadCompleteSem, also
  993. * contained in the WV_UPLOADTASK_ID, is given when the this routine has
  994. * emptied the buffer.  This allows others to wait until the buffer is
  995. * empty.
  996. *
  997. * If an error occurs when reading from the buffers or writing to the upload
  998. * path, event logging is stopped and the upload path is closed.  The status
  999. * field in the WV_UPLOADTASK_ID indicates whether the upload was successful
  1000. * or if an error occurred.  When writing to the upload path, if an EAGAIN
  1001. * or EWOULDBLOCK error is encountered due to a failure to write to a non-
  1002. * blocking path, this routine will backoff and retry to write to the path.
  1003. * The number of times to attempt writing, and the the amount of time to
  1004. * delay between attempts can be set with the global variables
  1005. *
  1006. *  int wvUploadMaxAttempts -- 200 by default
  1007. *  int wvUploadRetryBackoff  -- 6 by default (in ticks)
  1008. *
  1009. * If the block of data, usually one threshold in size, can not be written
  1010. * within the maximum number of attempts, this routine will stop event 
  1011. * logging, close the upload path and indicate the error within the status
  1012. * field of the WV_UPLOADTASK_ID.
  1013. *
  1014. * To avoid copying the event data to a local buffer, only to be copied again
  1015. * to the upload path, reading the buffer is done using a reserve/commit
  1016. * interface.  Reserving a number of bytes to read from the buffer provides
  1017. * access, via a pointer, directly to the event data in the buffer.  This
  1018. * pointer references only contiguous memory and can be passed directly to the
  1019. * upload mechanism.  Hence no extra copying is employed.  The buffer's
  1020. * commit interface is used to notify the buffering mechanism that the
  1021. * reserved memory has been consumed.
  1022. *
  1023. * The semaphore on which wvUpload blocks works together with the threshold,
  1024. * also part of the BUFFER_ID, as a general purpose mechanism for telling
  1025. * users of the buffer when threshold bytes of data are contained in the
  1026. * buffer.  This gives the the uploader a way of controlling the frequency 
  1027. * and size of uploads.  The threshold is assigned by the buffer mechanism,
  1028. * but can be an arbitrary value.  In general, the buffer may assign threshold
  1029. * to meet a buffer contraint, or it may allow the user to assign it, and 
  1030. * allow access to its value in the structure pointed to by the BUFFER_ID.
  1031. *
  1032. * All buffer accesses are protected with interrupts locked out.
  1033. *
  1034. * RETURNS: OK when buffer is successfully emptied and uploaded and
  1035. *          the upload task ID's exitWhenEmpty is set to TRUE.
  1036. *          ERROR if an error occurs with the upload path or buffers; event
  1037. *          logging is stopped if an error occurs.
  1038. *
  1039. * SEE ALSO:
  1040. * NOMANUAL
  1041. *
  1042. */
  1043. static STATUS wvUpload 
  1044.     (
  1045.     WV_UPLOADTASK_ID    upTaskId /* this task's descriptor */
  1046.     )
  1047.     {
  1048.     int       nReserved; /* num bytes reserved and possible to upload */
  1049.     int       nToWrite; /* num bytes that should be written to host */
  1050.     int       nUploaded; /* number of bytes actually written to host */
  1051.     int       nToCommit; /* number of bytes to commit in the buffer */
  1052.     int       tmp; /* temporary used to count uploaded bytes */
  1053.     BOOL      bufferEmpty;  /* used to exit when buffer is empty */
  1054.     UINT8     *pData; /* pointer to the read-reserved data */
  1055.     int       lockKey; /* for interrupt locking */
  1056.     BUFFER_ID bufId; /* convenient access to upTaskId->bufferId */
  1057.     UPLOAD_ID pathId; /* same for upTaskId->uploadPathId */
  1058.     /* Check that the upload-task id has appropriate information. */
  1059.     if (upTaskId == NULL)
  1060. return (ERROR);
  1061.     if (upTaskId->bufferId == NULL)
  1062. {
  1063. upTaskId->status = ERROR;
  1064. return (ERROR);
  1065. }
  1066.     if (upTaskId->uploadPathId == NULL)
  1067. {
  1068. upTaskId->status = ERROR;
  1069. return (ERROR);
  1070. }
  1071.     /* Initialize variables. */
  1072.     bufId   = upTaskId->bufferId;
  1073.     pathId  = upTaskId->uploadPathId;
  1074.     bufferEmpty = FALSE;
  1075.     nToWrite    = 0;
  1076.     while (TRUE)
  1077.         {
  1078.         /* 
  1079.  * Initialize variables that allow us to stop uploading at the right
  1080.  * time.  Assuming that there is data in the buffer (bufferEmtpy =
  1081.  * FALSE) is alright because nothing below will error if there is
  1082.  * not.  In other words, bufferEmpty accurately describes when the
  1083.  * buffer is empty but does not accurately describe when the buffer
  1084.  * is not empty.
  1085.  */
  1086.         nUploaded = 0;
  1087. bufferEmpty = FALSE;
  1088. /* 
  1089.  * If the caller requested to exit when the buffer is empty, give
  1090.  * him/er the result immediately, even if there is no data available.
  1091.  * Also, if there is still more than a threshold of data in the buffer,
  1092.  * continue uploading.  Otherwise block until sufficient data becomes
  1093.  * available.
  1094.  */
  1095.         lockKey = intLock ();
  1096. tmp = bufId->nBytesRtn (bufId);
  1097. intUnlock (lockKey);
  1098.         if (! upTaskId->exitWhenEmpty && (tmp < bufId->threshold))
  1099.     semTake (& bufId->threshXSem, WAIT_FOREVER);
  1100. /*
  1101.  * Upload in chunks of size bufId->threshold.  The readReserve
  1102.  * routine of the buffer only guarantees that every time the
  1103.  * threshXSem is able to be taken there are threshold bytes of data
  1104.  * in the buffer.  That data may not be contiguous, and so we have
  1105.  * to loop until threshold bytes have been uploaded.
  1106.          */
  1107.         
  1108.         while ((nUploaded < bufId->threshold) && !bufferEmpty)
  1109.     {
  1110.      
  1111.             /* Reserve as many contiguous bytes as possible from the buffer. */
  1112.     lockKey = intLock ();
  1113.             nReserved = bufId->readReserveRtn (bufId, &pData);
  1114.     intUnlock (lockKey);
  1115.             if (nReserved == ERROR)
  1116.         {
  1117.         /* 
  1118.          * If an error occurs close the upload path and stop event
  1119.  * logging.  There is no harm in turning event logging off
  1120.  * if it is not currently on.
  1121.          */
  1122.                 lockKey = intLock ();
  1123. wvEvtLogStop();
  1124. intUnlock (lockKey);
  1125. pathId->errorRtn (pathId);
  1126.         upTaskId->status = ERROR;
  1127. logMsg ("tWvUpload: failed to read from buffer.n", 
  1128. 0, 0, 0, 0, 0, 0);
  1129. return (ERROR);
  1130.                 }
  1131.             /* Notice when the buffer is emptied. */
  1132.             if (nReserved == 0)
  1133. bufferEmpty = TRUE;
  1134.             else
  1135. bufferEmpty = FALSE;
  1136.       /* 
  1137.      * Write as many bytes as possible that still need to be uploaed,
  1138.      * to the host, but not more than threshold.  And, remember how 
  1139.      * many to commit later.
  1140.      */
  1141.             nToWrite = (nReserved > (bufId->threshold - nUploaded)) ? 
  1142.        (bufId->threshold - nUploaded) : nReserved;
  1143.             nToCommit = nToWrite;
  1144.             if (uploadPathWrite (pathId, pData, nToWrite) == ERROR)
  1145.                 {
  1146.                 /*
  1147.                  * There is still no harm in turning off event logging
  1148.                  * if it is not currently on.
  1149.                  */
  1150.                 lockKey = intLock ();
  1151.                 wvEvtLogStop();
  1152.                 intUnlock (lockKey);
  1153.                 pathId->errorRtn (pathId);
  1154.                 upTaskId->status = ERROR;
  1155.                 logMsg ("tWVUpload: failed writing to host.n",
  1156.                         0, 0, 0, 0, 0, 0);
  1157.                 return (ERROR);
  1158.                 }
  1159.             /* 
  1160.      * At this point all the reserved bytes that were possible to 
  1161.      * read in this pass through the loop have been uploaded.  Those
  1162.      * bytes are committed before determining whether to loop again.
  1163.      */
  1164.             lockKey = intLock ();
  1165.             tmp = bufId->readCommitRtn (bufId, nToCommit);
  1166.     intUnlock (lockKey);
  1167.  
  1168.             if (tmp == ERROR)
  1169. {
  1170. /* 
  1171.  * There is no harm in turning logging off if it is already 
  1172.  * off.
  1173.  */
  1174.                 lockKey = intLock ();
  1175. wvEvtLogStop();
  1176. intUnlock (lockKey);
  1177. pathId->errorRtn (pathId);
  1178.         upTaskId->status = ERROR;
  1179. logMsg ("tWvUpload: failed to commit uploaded bytes.n", 
  1180. 0, 0, 0, 0, 0, 0);
  1181. return (ERROR);
  1182.                 }
  1183.             nUploaded += nToCommit;
  1184.     }
  1185.             /* 
  1186.      * At this point the buffer is empty or threshold bytes have been
  1187.      * uploaded.  Exit only if the buffer was emptied and the user 
  1188.      * requested not to wait for more data.
  1189.      */
  1190.             if (upTaskId->exitWhenEmpty && bufferEmpty)
  1191. {
  1192.         upTaskId->status = OK;
  1193. semGive (& upTaskId->uploadCompleteSem);
  1194. return (OK);
  1195. }
  1196.         }
  1197.     }
  1198. /*******************************************************************************
  1199. *
  1200. * wvUploadStart - start upload of events to the host (WindView)
  1201. *
  1202. * This routine starts uploading events from the event buffer to the host.
  1203. * Events can be uploaded either continuously or in one pass until the
  1204. * buffer is emptied.  If <uploadContinuously> is set to TRUE, the task
  1205. * uploading events pends until more data arrives in the buffer.  If FALSE,
  1206. * the buffer is flushed without waiting,  but this routine 
  1207. * returns immediately with an ID that can be used to kill the upload task.
  1208. * Upload is done by spawning the task 'tWVUpload'.  The buffer to upload is 
  1209. * identified by <bufId>, and the upload path to use is identified by <pathId>.
  1210. *
  1211. * This routine blocks if no event data is in the buffer, so it should
  1212. * be called before event logging is started to ensure the buffer does
  1213. * not overflow.
  1214. *
  1215. * RETURNS: A valid WV_UPLOADTASK_ID if started for continuous
  1216. * upload, a non-NULL value if started for one-pass upload, and NULL 
  1217. * if the task can not be spawned or memory for the descriptor 
  1218. * can not be allocated.
  1219. *
  1220. */
  1221. WV_UPLOADTASK_ID wvUploadStart 
  1222.     (
  1223.     BUFFER_ID bufId,      /* event data buffer ID */
  1224.     UPLOAD_ID pathId,      /* upload path to host */
  1225.     BOOL uploadContinuously  /* upload continuously if true */
  1226.     )
  1227.     {
  1228.     WV_UPLOADTASK_ID upTaskId; /* returned to later identify the upload task */
  1229.     /* Check for valid parameters. */
  1230.     if (bufId == NULL || pathId == NULL)
  1231. return (NULL);
  1232.     /* 
  1233.      * Create and initialize the upload task descriptor to return to the 
  1234.      * user.  This id is necessary for stopping the task later.
  1235.      */
  1236.     if ((upTaskId = (WV_UPLOADTASK_ID) 
  1237.     malloc (sizeof (WV_UPLOADTASK_DESC))) == NULL)
  1238.         {
  1239. logMsg ("wvUploadStart: can't alloc uploadTask id memory.n",
  1240.  0,0,0,0,0,0);
  1241. return (NULL);
  1242. }
  1243.     if (semBInit (& upTaskId->uploadCompleteSem, SEM_Q_PRIORITY, 
  1244.   SEM_EMPTY) == ERROR)
  1245.         {
  1246. logMsg ("wvUploadStart: can't init uploadTask sem.n",0,0,0,0,0,0);
  1247. return (NULL);
  1248. }
  1249.     upTaskId->bufferId          = bufId;
  1250.     upTaskId->uploadPathId      = pathId;
  1251.     upTaskId->status = OK;
  1252.     if (uploadContinuously)
  1253.         upTaskId->exitWhenEmpty = FALSE;
  1254.     else
  1255.         upTaskId->exitWhenEmpty = TRUE;
  1256.     /* Now spawn the task. */
  1257.     if ((upTaskId->uploadTaskId = taskSpawn ("tWVUpload", wvUploadTaskPriority, 
  1258.              wvUploadTaskOptions,
  1259.                              wvUploadTaskStackSize, wvUpload,
  1260.      (int) upTaskId, 0, 0, 0, 0, 0,
  1261.      0, 0, 0, 0)) == ERROR)
  1262.         {
  1263. logMsg ("wvUploadStart: can't spawn uploadTask.n",0,0,0,0,0,0);
  1264. return (NULL);
  1265.         }
  1266.     return (upTaskId);
  1267.     }
  1268. /*******************************************************************************
  1269. *
  1270. * wvUploadStop - stop upload of events to host (WindView)
  1271. *
  1272. * This routine stops continuous upload of events to the host.  It does this
  1273. * by making a request to the upload task to terminate after it has emptied
  1274. * the buffer.  For this reason it is important to make sure data is no
  1275. * longer being logged to the buffer before calling this routine.
  1276. * This task blocks until the buffer is emptied, and then frees memory
  1277. * associated with <upTaskId>.
  1278. *
  1279. * RETURNS: OK if the upload task terminates successfully, 
  1280. * or ERROR either if <upTaskId> is invalid or if the upload task terminates
  1281. * with an ERROR.
  1282. *
  1283. */
  1284. STATUS wvUploadStop 
  1285.     (
  1286.     WV_UPLOADTASK_ID upTaskId
  1287.     )
  1288.     {
  1289.     STATUS retStatus;
  1290.     if (upTaskId == NULL)
  1291. return (ERROR);
  1292.     /* 
  1293.      * Ask the upload task to flush the buffer and then exit.  The upload
  1294.      * task may have emptied the buffer and be waiting on the buffer's
  1295.      * threshold-crossed semaphore, so we give that semaphore so the task
  1296.      * won't pend forever.
  1297.      */
  1298.     upTaskId->exitWhenEmpty = TRUE;
  1299.     semGive (& upTaskId->bufferId->threshXSem);
  1300.     /* Wait for flushing to complete, but only if uploader worked correctly */
  1301.     if (upTaskId->status == OK)
  1302.         semTake (& upTaskId->uploadCompleteSem, WAIT_FOREVER);
  1303.     /* Free up the memory associated with the upload descriptor. */
  1304.     semTerminate (& upTaskId->uploadCompleteSem);
  1305.     retStatus = upTaskId->status;
  1306.     free (upTaskId);
  1307.     return (retStatus);
  1308.     }
  1309. /*******************************************************************************
  1310. *
  1311. * wvUploadTaskConfig - set priority and stacksize of 'tWVUpload' task (WindView)
  1312. *
  1313. * This routine sets the stack size and priority of future instances of 
  1314. * the event-data upload task, created by calling wvUploadStart().  The default
  1315. * stack size for this task is 5000 bytes, and the default priority is 150.
  1316. * RETURNS: N/A
  1317. */
  1318. void wvUploadTaskConfig
  1319.     (
  1320.     int stackSize, /* the new stack size for tWVUpload */
  1321.     int priority /* the new priority for tWVUpload */
  1322.     )
  1323.     {
  1324.     wvUploadTaskStackSize = stackSize;
  1325.     wvUploadTaskPriority  = priority;
  1326.     }
  1327. /*******************************************************************************
  1328. *
  1329. * wvLogHeaderCreate - create the event-log header (WindView)
  1330. *
  1331. * This routine creates the header of EVENT_CONFIG, EVENT_BUFFER, and EVENT_BEGIN
  1332. * events that is required at the beginning of every event log.  These events are
  1333. * stored in a packed array allocated from the specified memory partition.
  1334. * In addition to this separate header, this routine also logs all tasks
  1335. * active in the system to the event buffer for uploading along with the
  1336. * other events.
  1337. *
  1338. * This routine should be called after wvEvtLogInit() is called.  If uploading
  1339. * events continuously to the host, this routine should be called after the 
  1340. * upload task is started.  This ensures that the upload task is included in 
  1341. * the snapshot of active tasks.  If upload will occur after event logging has 
  1342. * stopped (deferred upload), this routine can be called any time before event 
  1343. * logging is turned on.
  1344. *
  1345. * RETURNS: A valid WV_LOG_HEADER_ID, or NULL if memory can not be allocated.
  1346. */
  1347. WV_LOG_HEADER_ID wvLogHeaderCreate 
  1348.     (
  1349.     PART_ID   memPart /* partition where header should be stored */
  1350.     )
  1351.     {
  1352.     WV_LOG_HEADER *pHead; /* local copy of the log header struct */
  1353.     int   *pIntCur; /* integer pointer into buf */
  1354.     short   *pShortCur; /* short pointer into buf */
  1355.     char   *pByteCur; /* char pointer into buf */
  1356.     short cfgEventId;           /* EVENT_CONIG */
  1357.     int   cfgProtocolRev;
  1358.     int   cfgTimestampFreq;
  1359.     int   cfgTimestampPeriod;
  1360.     int   cfgAutoRollover;
  1361.     int   cfgClkRate;
  1362.     int   cfgCollectionMode;
  1363.     int   cfgProcessorNum;
  1364.     short bufEventId;           /* EVENT_BUFFER */
  1365.     int   bufTaskIdCurrent;
  1366.     short begEventId;           /* EVENT_BEGIN */
  1367.     int   begCpu;
  1368.     int   begBspSize;           /* char *begBspName copied directly to buf */
  1369.     int   begTaskIdCurrent;
  1370.     int   begCollectionMode;
  1371.     int   begRevision;
  1372.     char  commentString[] = "Windview Version 2.2 Logfile.  Copyright 
  1373.  1999-2001 Wind River Systems.";
  1374.     int   commentLen;
  1375.     int   bspLen;
  1376.     
  1377.     /* 
  1378.      * Get the actual log header structure first, and then figure out how
  1379.      * big to make it, considering the variable-length bspName field.
  1380.      */
  1381.     pHead = (WV_LOG_HEADER *) memPartAlloc (memPart, sizeof (WV_LOG_HEADER));
  1382.     if (pHead == NULL)
  1383. return (NULL);
  1384.     commentLen = strlen (commentString);
  1385.     if (commentLen & 1)
  1386.         commentLen++;
  1387.     
  1388.     bspLen = strlen (sysModel ());
  1389.     if (bspLen & 1)
  1390.         bspLen++;
  1391.     
  1392.     pHead->len = (EVENT_BEGIN_SIZE + EVENT_CONFIG_SIZE + EVENT_BUFFER_SIZE +
  1393.   commentLen + EVENT_COMMENT_SIZE +
  1394.                   bspLen);
  1395.     if (pHead->len & 1)
  1396. ++pHead->len;
  1397.     pHead->header = memPartAlloc (memPart, pHead->len);
  1398.     if (pHead->header == NULL)
  1399. {
  1400. memPartFree (memPart, (char *) pHead);
  1401. return (NULL);
  1402. }
  1403.     pHead->memPart = memPart;
  1404.     /*
  1405.      * Get memory for an EVENT_CONFIG buffer from the event buffer, using
  1406.      * the writeReserve interface.  Then fill in that memory.
  1407.      */
  1408.     cfgEventId         = EVENT_CONFIG;
  1409.     cfgProtocolRev     = WV_REV_ID_CURRENT | WV_EVT_PROTO_REV_CURRENT;
  1410.     cfgTimestampFreq   = (* _func_tmrFreq) ();
  1411.     cfgTimestampPeriod = (* _func_tmrPeriod) ();
  1412.     cfgAutoRollover    = ((* _func_tmrConnect) ((FUNCPTR) _func_evtLogT0,
  1413.                                                 EVENT_TIMER_ROLLOVER)) + 1;
  1414.     cfgClkRate         = sysClkRateGet ();
  1415.     cfgCollectionMode  = (int)(wvEvtClass & WV_CLASS_3);
  1416.     cfgProcessorNum    = sysProcNumGet ();
  1417.     pShortCur = (short *) pHead->header;
  1418.     EVT_STORE_UINT16 (pShortCur, cfgEventId);
  1419.     pIntCur   = (int *) pShortCur;
  1420.     EVT_STORE_UINT32 (pIntCur, cfgProtocolRev);
  1421.     EVT_STORE_UINT32 (pIntCur, cfgTimestampFreq);
  1422.     EVT_STORE_UINT32 (pIntCur, cfgTimestampPeriod);
  1423.     EVT_STORE_UINT32 (pIntCur, cfgAutoRollover);
  1424.     EVT_STORE_UINT32 (pIntCur, cfgClkRate);
  1425.     EVT_STORE_UINT32 (pIntCur, cfgCollectionMode);
  1426.     EVT_STORE_UINT32 (pIntCur, cfgProcessorNum);
  1427.     /*
  1428.      * Log the EVENT_LOGCOMMENT event to the buffer.
  1429.      */
  1430.     begEventId        = EVENT_LOGCOMMENT;
  1431.     pShortCur = (short *) pIntCur;
  1432.     EVT_STORE_UINT16 (pShortCur, begEventId);
  1433.     pIntCur   = (int *) pShortCur;
  1434.     EVT_STORE_UINT32 (pIntCur, commentLen);
  1435.     pByteCur = (char *) pIntCur;
  1436.     strncpy (pByteCur, commentString, commentLen);
  1437.     pByteCur += commentLen;
  1438.     pIntCur   = (int *) pByteCur;
  1439.     /*
  1440.      * Do the same for an EVENT_BUFFER.  Reserve memory for the event from
  1441.      * the event buffer, and then fill in that memory.
  1442.      */
  1443.     bufEventId       = EVENT_BUFFER;
  1444.     bufTaskIdCurrent = (kernelIsIdle) ? (int) ERROR : (int) taskIdCurrent;
  1445.     pShortCur = (short *) pIntCur;
  1446.     EVT_STORE_UINT16 (pShortCur, bufEventId);
  1447.     pIntCur   = (int *) pShortCur;
  1448.     EVT_STORE_UINT32 (pIntCur, bufTaskIdCurrent);
  1449.     /*
  1450.      * Log the EVENT_BEGIN event to the buffer.  The cpu-type member is
  1451.      * tricky because it has to be of even length to transfer to the host.
  1452.      * Also the memory reserved has to vary by its length.
  1453.      */
  1454.     begEventId        = EVENT_BEGIN;
  1455.     begCpu            = sysCpu;
  1456.     begBspSize        = bspLen;
  1457.     begTaskIdCurrent  = (int) taskIdCurrent;
  1458.     begCollectionMode  = (int)(wvEvtClass & WV_CLASS_3);
  1459.     begRevision       = WV_EVT_PROTO_REV_CURRENT;
  1460.     pShortCur = (short *) pIntCur;
  1461.     EVT_STORE_UINT16 (pShortCur, begEventId);
  1462.     pIntCur   = (int *) pShortCur;
  1463.     EVT_STORE_UINT32 (pIntCur, begCpu);
  1464.     EVT_STORE_UINT32 (pIntCur, begBspSize);
  1465.     pByteCur = (char *) pIntCur;
  1466.     strncpy (pByteCur, sysModel(), begBspSize);
  1467.     pByteCur += begBspSize;
  1468.     pIntCur   = (int *) pByteCur;
  1469.     EVT_STORE_UINT32 (pIntCur, begTaskIdCurrent);
  1470.     EVT_STORE_UINT32 (pIntCur, begCollectionMode);
  1471.     EVT_STORE_UINT32 (pIntCur, begRevision);
  1472.     /* Log a snapshot of active tasks to the event buffer. */
  1473.     evtLogTasks ();
  1474.     return (pHead);
  1475.     }
  1476. /*******************************************************************************
  1477. *
  1478. * wvLogHeaderUpload - transfer the log header to the host (WindView)
  1479. *
  1480. * This functions transfers the log header events (EVENT_BEGIN, EVENT_CONFIG,
  1481. * EVENT_BUFFER) to the host.  These events were saved to a local buffer
  1482. * with the call to wvLogHeaderCreate().  This routine should be called before
  1483. * any events or tasknames are uploaded to the host.  The events in the 
  1484. * header buffer must be the first things the parser sees.
  1485. *
  1486. * If continuously uploading events, it is best to start the uploader, and 
  1487. * then call this routine.  If deferring upload until after event logging
  1488. * is stopped, this should be called before the uploader is started.
  1489. *
  1490. * RETURNS: OK, or ERROR if there is trouble with the upload path.
  1491. */
  1492. STATUS wvLogHeaderUpload
  1493.     (
  1494.     WV_LOG_HEADER_ID pHeader, /* pointer to the header */
  1495.     UPLOAD_ID pathId /* path by which to upload to host */
  1496.     )
  1497.     {
  1498.     int     res; /* partly notice write errors */
  1499.     if (pHeader == NULL || pathId == NULL)
  1500. return (ERROR);
  1501.     res = pathId->writeRtn (pathId, pHeader->header, pHeader->len);
  1502.     if (res < 0)
  1503. return (ERROR);
  1504.     if (pmPartId != NULL)
  1505.         {
  1506.      /* Free the header. */
  1507.      memPartFree (pHeader->memPart, (char *) pHeader->header);
  1508.      memPartFree (pHeader->memPart, (char *) pHeader);
  1509. }
  1510.     return (OK);
  1511.     }
  1512. /*******************************************************************************
  1513. *
  1514. * wvEvtBufferGet - return the ID of the WindView event buffer (WindView)
  1515. *
  1516. * RETURNS: The event buffer ID if one exists, otherwise NULL.
  1517. */
  1518. BUFFER_ID wvEvtBufferGet (void)
  1519.     {
  1520.     return ((wvEvtBufferId == 0) ? (BUFFER_ID) NULL : wvEvtBufferId);
  1521.     }
  1522. /*******************************************************************************
  1523. *
  1524. * wvTaskNamesPreserve - preserve an extra copy of task name events (WindView)
  1525. *
  1526. * This routine initializes the data structures and instrumentation necessary
  1527. * to allow WindView to store an extra copy of each EVENT_TASKNAME event,
  1528. * which is necessary for post-mortem analysis.  This routine should be called
  1529. * after wvEvtLogInit() has been called, and before event logging is started.
  1530. *
  1531. * If this routine is called before event logging is started, all
  1532. * EVENT_TASKNAME events that are produced by VxWorks are logged into the
  1533. * standard event buffer, and a copy of each is logged automatically to the
  1534. * task name buffer created by this routine.  All tasks running when this 
  1535. * routine is called are also added to the buffer.  The events in this buffer
  1536. * can be uploaded after the other events have been uploaded, to provide the
  1537. * task names for any events in the log which no longer have a corresponding
  1538. * task name event due to wrapping of data in the buffers.  Because there
  1539. * may be two copies of some of the task name events after the buffer data
  1540. * wraps around, the resultant log may have two task name events for the same
  1541. * task.  This is not a problem for the parser.
  1542. *
  1543. * Occasionally the task ID of a task is reused, and in this case, only
  1544. * the last instance of the task name event with a particular task ID is
  1545. * maintained.
  1546. *
  1547. * The buffer size must be a power of two.
  1548. *
  1549. * This routine sets the event class WV_CLASS_TASKNAMES_PRESERVE, which can
  1550. * be turned off by calling wvEvtClassClear() or wvEvtClassSet().
  1551. *
  1552. * INTERNAL
  1553. * This routine returns the buffer's ID, which is required later to
  1554. * destroy and upload the buffer.  The ID is not required to add an event
  1555. * to the buffer.  That is because additions to the buffer will be done
  1556. * from an event point, and the event point will not have access to the
  1557. * buffer's ID.  This is exactly how the event buffer is accessed in 
  1558. * evtLogLib.
  1559. *
  1560. * The buffer used to store the reserved taskname events is a hash table that 
  1561. * is implemented in wvLib.  All functions associated with this hash table are
  1562. * prefixed with tnHashTbl.  The implementation of the reserved taskname buffer
  1563. * has been hidden (albeit barely) from this function, so that any implemen-
  1564. * tation of a buffer can be used.  The type of the buffer is identified as 
  1565. * a TASKBUF_ID.  The specific implementation of the hash-table buffer is 
  1566. * a TN_HASH_TBL.
  1567. * A hash table has been used because, given very specific circumstances, the
  1568. * task IDs of tasks with different names may be reused.  A hash table allows
  1569. * easy and fast replacement of the names, so that there is only one name per
  1570. * tid in the table at any time.  As new names, corresponding to a given tid,
  1571. * are added, the event is replaced with the newest event.
  1572. *
  1573. * The hash table was used to avoid multiple taskname events with the same tid,
  1574. * however there may be a copy of the same event still in the buffer, providing
  1575. * it has not been overwritten.  In this case there will be two of the same
  1576. * events anyway, so the hash table is overkill.  Oh well.
  1577. *
  1578. * RETURNS: A valid TASKBUF_ID to be used for later uploading, or NULL if 
  1579. * not enough memory exists to create the task buffer.
  1580. */
  1581. TASKBUF_ID wvTaskNamesPreserve
  1582.     (
  1583.     PART_ID memPart, /* memory where preserved names are stored */
  1584.     int     size  /* must be a power of 2 */
  1585.     )
  1586.     {
  1587.     int     nTasks; /* number of active tasks */
  1588.     int    idList [MAX_WV_TASKS];  /* list of active task IDs */
  1589.     int    ix; /* counting index */
  1590.     TN_HASH_TBL   *pTbl; /* the taskname buffer */
  1591.     /* Create the buffer where names will be stored. */
  1592.     if ((pTbl = tnHashTblCreate (memPart, size)) == NULL)
  1593. return (NULL);
  1594.     /* 
  1595.      * Stash a copy of the the buffer id for table adds, directly from
  1596.      * event points later.   This has to be done before we add any names.
  1597.      */
  1598.     tnHashTbl   = pTbl;
  1599.     /* Store a copy of each taskname already running in the system. */
  1600.     nTasks = taskIdListGet (idList, NELEMENTS (idList));
  1601.     for (ix = 0; ix < nTasks; ++ix)
  1602. {
  1603. if (taskIdVerify (idList [ix]) == OK)
  1604.     {
  1605.     wvTaskNamesBufAdd (EVENT_TASKNAME,
  1606.        ((WIND_TCB *)idList[ix])->status,
  1607.        ((WIND_TCB *)idList[ix])->priority,
  1608.        ((WIND_TCB *)idList[ix])->lockCnt,
  1609.        idList [ix], taskName (idList [ix]));
  1610.             }
  1611.         }
  1612.     /* 
  1613.      * Let the event point know that it should reserve a copy of each 
  1614.      * future event. It will be turned off when event loggin is turned 
  1615.      * off or reset.
  1616.      */
  1617.     WV_EVTCLASS_SET (WV_CLASS_TASKNAMES_PRESERVE);
  1618.     return ((TASKBUF_ID) pTbl);
  1619.     }
  1620. /*******************************************************************************
  1621. *
  1622. * wvTaskNamesUpload - upload preserved task name events (WindView)
  1623. *
  1624. * This routine uploads task name events, saved after calling 
  1625. * wvTaskNamesPreserve(), to the host by the specified upload path.  There 
  1626. * is no particular order to the events uploaded.  All the events contained 
  1627. * in the buffer are uploaded in one pass.  After all have been uploaded, the 
  1628. * buffer used to store the events is destroyed.
  1629. *
  1630. * RETURNS: OK, or ERROR if the upload path or task name buffer is invalid.
  1631. */
  1632. STATUS wvTaskNamesUpload 
  1633.     (
  1634.     TASKBUF_ID taskBufId,  /* taskname event buffer to upload */
  1635.     UPLOAD_ID pathId /* upload path id */
  1636.     )
  1637.     {
  1638.     TN_ITER_KEY   *pIterKey; /* key to iterate over buffer */
  1639.     TN_EVENT      *pEvent; /* event to upload */
  1640.     int            res = OK; /* to track errors uploading */
  1641.     TN_HASH_TBL   *pTbl; /* just in case a taskbuf is not a 
  1642.    taskname hash table */
  1643.     if (taskBufId == NULL || pathId == NULL)
  1644. return (ERROR); 
  1645.     /* 
  1646.      * If the buffer implemented to store taskname events is not a TN_HASH_TBL
  1647.      * then we need to do the casting here.  It usually is.
  1648.      */
  1649.     pTbl = (TN_HASH_TBL *) taskBufId;
  1650.     /* Create an iterator key to use to access all events in buffer. */
  1651.     if ((pIterKey = tnHashTblIterInit (pTbl)) == NULL)
  1652. return (ERROR);
  1653.     while ((pEvent = tnHashTblIterNextEvent (taskBufId, pIterKey)) != NULL)
  1654. {
  1655.         /* Write the values one at a time to eliminate padding in the log. */
  1656.         res = pathId->writeRtn (pathId, & pEvent->eventId, sizeof (short));
  1657.         res = pathId->writeRtn (pathId, & pEvent->status, sizeof (int));
  1658.         res = pathId->writeRtn (pathId, & pEvent->priority, sizeof (int));
  1659.         res = pathId->writeRtn (pathId, & pEvent->taskLockCount, sizeof (int));
  1660.         res = pathId->writeRtn (pathId, & pEvent->tid, sizeof (int));
  1661.         res = pathId->writeRtn (pathId, & pEvent->nameSize, sizeof (int));
  1662.         res = pathId->writeRtn (pathId,   pEvent->name, pEvent->nameSize);
  1663.         if (res < 0)
  1664.     {
  1665.     tnHashTblIterDone (pIterKey, pTbl);
  1666.             return (ERROR);
  1667.     }
  1668. }
  1669.     tnHashTblIterDone (pIterKey, pTbl);
  1670.     tnHashTblDestroy (pTbl);
  1671.     return (OK);
  1672.     }
  1673. /*******************************************************************************
  1674. *
  1675. * wvTaskNamesBufAdd - add a taskname event to the taskname buffer (WindView)
  1676. *
  1677. * This routine reserves a taskname event by adding it to the buffer used to
  1678. * store a copy of all such events for WindView post-mortem use.  The buffer
  1679. * must already have been created with wvTaskNamesPreserve().  If the task ID of
  1680. * the task event being added is already in the table, this routine replaces
  1681. * the existing copy of the event with the one being added.
  1682. *
  1683. * INTERNAL:
  1684. *
  1685. * The ID of the buffer is not passed as one of the parameters to this
  1686. * routine because this routine will most likely be called from the event
  1687. * point, which does not have access to the ID at logging time.  The other
  1688. * buffer-access routines, such as destroy and upload, require the buffer
  1689. * ID as a parameter, but we use a static copy of the ID here.
  1690. *
  1691. * Because this is called when creating a task, we are not in kernelState
  1692. * nor in an ISR.  Therefore, anything goes for getting memory and calling
  1693. * utility functions.  
  1694. *
  1695. * RETURNS: OK, or ERROR if memory can not be allocated or the
  1696. * task name buffer does not exist.
  1697. *
  1698. * NOMANUAL
  1699. */
  1700. STATUS wvTaskNamesBufAdd
  1701.     (
  1702.     short eventId, /* event values */
  1703.     int status,
  1704.     int priority,
  1705.     int taskLockCount,
  1706.     int tid,
  1707.     char *name
  1708.     )
  1709.     {
  1710.     TN_EVENT   *pEvent; /* struct to hold event */
  1711.     TN_EVENT   *pReplaced; /* iff replaced instead of added */
  1712.     /* Create an event structure to hold the event, and fill it. */
  1713.     pEvent = (TN_EVENT *) memPartAlloc (tnHashTbl->memPart, sizeof (TN_EVENT));
  1714.     if (pEvent == NULL)
  1715.         return (ERROR);
  1716.     pEvent->eventId       = eventId;
  1717.     pEvent->status        = status;
  1718.     pEvent->priority      = priority;
  1719.     pEvent->taskLockCount = taskLockCount;
  1720.     pEvent->tid           = tid;
  1721.     pEvent->nameSize      = strlen (name);
  1722.     pEvent->name = (char *) memPartAlloc (tnHashTbl->memPart, pEvent->nameSize);
  1723.     if (pEvent->name == NULL)
  1724.         {
  1725.         memPartFree (tnHashTbl->memPart, (char *) pEvent);
  1726.         return (ERROR);
  1727.         }
  1728.     strncpy (pEvent->name, name, pEvent->nameSize);
  1729.     /*
  1730.      * If the tid already exists replace the existing event with this
  1731.      * one.  Otherwise simply add it.  This is the policy of the insert
  1732.      * routine.
  1733.      */
  1734.     pReplaced = NULL;
  1735.     if (tnHashTblInsert (tnHashTbl, pEvent, pEvent->tid, pReplaced) == ERROR)
  1736.         {
  1737.         memPartFree (tnHashTbl->memPart, (char *) pEvent->name);
  1738.         memPartFree (tnHashTbl->memPart, (char *) pEvent);
  1739.         return (ERROR);
  1740.         }
  1741.     /* Check if the event was replaced, and if so free the returned event. */
  1742.     if (pReplaced != NULL)
  1743.         {
  1744.         memPartFree (tnHashTbl->memPart, (char *) pReplaced->name);
  1745.         memPartFree (tnHashTbl->memPart, (char *) pReplaced);
  1746.         }
  1747.     return (OK);
  1748.     }
  1749. /*******************************************************************************
  1750. *
  1751. * tnHashTblCreate - create hash table to store reserved task name events
  1752. *
  1753. * This routine creates a hash table that will hold the reserved copy of all
  1754. * task-name events generated by the system after wvTaskNamePreserve is
  1755. * called.  The hash table allows easy and fast lookup of the task name events
  1756. * based on a the task ID as a key.  It allocates the memory for the buffer 
  1757. * from the memory partition identified by memPart.
  1758. *
  1759. * The structure of the hash table is very similar to the tables implemented
  1760. * in hashLib.  Below is what this table looks like.
  1761. *
  1762. * .CS
  1763. *
  1764. *   TN_HASH_TBL
  1765. *   --------
  1766. *   | size |     ---------------------
  1767. *   | tbl  |---> | 0| 1| 2| 3| 4| 5| 6| ...
  1768. *   --------     ---------------------
  1769. *                  |
  1770. *        |----------
  1771. *        v
  1772. *  (dummy headers)
  1773. *
  1774. *     TN_NODE (0)        TN_NODE            TN_NODE
  1775. *     --------           --------           --------
  1776. *     | next |---------->| next |---------> | next |----------
  1777. *     | key=0|           | key  |           | key  |         |
  1778. *     | event|--         | event|---        | event|---      v
  1779. *     -------- |         --------  |        --------  |     ---
  1780. *              v                   v                  v      -
  1781. *             ---               TN_EVENT           TN_EVENT
  1782. *              -                -------            -------
  1783. *                               | ... |            | ... |
  1784. *                               | ... |            | ... |
  1785. *                               | ... |            | ... |
  1786. *                               -------            -------
  1787. *
  1788. *     TN_NODE (1)
  1789. *     ...
  1790. *     TN_NODE [size]
  1791. *
  1792. * .CE
  1793. * The size of the table must be a power of two.
  1794. *
  1795. * RETURNS: TN_HASH_TBL, or error if not enough memory could be allocated
  1796. *          to create the structure.
  1797. * NOMANUAL
  1798. */
  1799. static TN_HASH_TBL * tnHashTblCreate
  1800.     (
  1801.     PART_ID memPart,             /* the memory partion where buf is placed */
  1802.     int tblSize                 /* size of the table */
  1803.     )
  1804.     {
  1805.     TN_HASH_TBL  *pTbl; /* the head of the hash table */
  1806.     TN_NODE     **pEdge; /* the array making the edge of pTbl */
  1807.     int ix; /* counting index */
  1808.     int jx; /* counting index */
  1809.     /* Create the table struct. */
  1810.     pTbl = (TN_HASH_TBL *) memPartAlloc (memPart, sizeof (TN_HASH_TBL));
  1811.     if (pTbl == NULL)
  1812.         return (NULL);
  1813.     /* Create the edge vector of the table. */
  1814.     pEdge = (TN_NODE **) memPartAlloc (memPart, tblSize * sizeof (TN_NODE *));
  1815.     if (pEdge == NULL)
  1816.         return (NULL);
  1817.     /* Create and initialize the dummy heads on the linked lists. */
  1818.     for (ix=0; ix<tblSize; ++ix)
  1819.         {
  1820.         pEdge[ix] = (TN_NODE *) memPartAlloc (memPart, sizeof (TN_NODE));
  1821.         if (pEdge[ix] == NULL)
  1822.             {
  1823.     for (jx = 0; jx <= ix; ++jx)
  1824. memPartFree (memPart, (char *) pEdge [ix]);
  1825.             memPartFree (memPart, (char *) pEdge);
  1826.             memPartFree (memPart, (char *) pTbl);
  1827.             return (NULL);
  1828.             }
  1829.         pEdge[ix]->next  = NULL;
  1830.         pEdge[ix]->key   = 0;
  1831.         pEdge[ix]->event = NULL;
  1832.         }
  1833.     /* Set the size, memPart, and edge vector of the table. */
  1834.     pTbl->size   = tblSize;
  1835.     pTbl->memPart = memPart;
  1836.     pTbl->tbl    = pEdge;
  1837.     return ((TASKBUF_ID) pTbl);
  1838.     }
  1839. /*******************************************************************************
  1840. *
  1841. * tnHash - return a unique index into the table based on the tid
  1842. *
  1843. * This is the hashing function for the hash table which reserves a copy
  1844. * of taskname events for windview post-mortem use.  The hash function
  1845. * simply returns the value of the key modulo 31 (an arbitrary divisor),
  1846. * masking the bits in the result to the size of the table.
  1847. *
  1848. * RETURNS: an index into the hash table unique to the key
  1849. *
  1850. * NOMANUAL
  1851. */
  1852. static int tnHash
  1853.     (
  1854.     TN_HASH_TBL *tbl,           /* provides the size of the tbl */
  1855.     int key                     /* usually the tid */
  1856.     )
  1857.     {
  1858.     FAST int hash;
  1859.     hash = key % 31;
  1860.     return (hash & (tbl->size - 1));
  1861.     }
  1862. /*******************************************************************************
  1863. * tnHashTblInsert - add a node to the hash table
  1864. *
  1865. * This routine inserts a node into the hash table that is used as the
  1866. * taskname event buffer.  It replaces the node if the key already exists
  1867. * in the table.  If the node was replaced, the replaced node is returned
  1868. * in pReplacedEvent, otherwise this value will be set to NULL.  This
  1869. * provides an easy way to let the caller free that memory.
  1870. *
  1871. * The hash table is described in the function header of tnHashTblCreate.
  1872. *
  1873. * RETURNS:  OK, or ERROR if failed to allocate memory from the partition
  1874. *
  1875. * NOMANUAL
  1876. */
  1877. static STATUS tnHashTblInsert
  1878.     (
  1879.     TN_HASH_TBL   *pTbl, /* the hash table to add to */
  1880.     TN_EVENT      *pEvent, /* the event data to add */
  1881.     int            key, /* this is part of the data */
  1882.     TN_EVENT      *pReplacedEvent /* replaced event iff exists */
  1883.     )
  1884.     {
  1885.     int         hashIx; /* hash tbl index for insert */
  1886.     TN_NODE    *pHead; /* dummy head node at hashIx */
  1887.     TN_NODE    *pNewNode; /* the node to be inserted */
  1888.     TN_NODE    *pCurNode; /* for updating the table */
  1889.     TN_NODE    *pPrevNode; /* same */
  1890.     BOOL         found; /* true iff the node already exists */
  1891.     if (pTbl == NULL)
  1892.         return (ERROR);
  1893.     /* Get the hash index. */
  1894.     hashIx  = tnHash (pTbl, key);
  1895.     pHead   = pTbl->tbl [hashIx];
  1896.     /* Try to find the key in the table. */
  1897.     found     = FALSE;
  1898.     pCurNode  = pHead->next;
  1899.     pPrevNode = pHead;
  1900.     while (!found && pCurNode != NULL)
  1901.         {
  1902.         if (pCurNode->key == key)
  1903.             {
  1904.             found = TRUE;
  1905.             }
  1906.         else
  1907.             {
  1908.             pPrevNode = pCurNode;
  1909.             pCurNode = pCurNode->next;
  1910.             }
  1911.         }
  1912.     if (found)
  1913.         {
  1914.         /*
  1915.          * If the key was found, pCurNode is pointing to the node we need
  1916.          * to replace, but it is only necessary to replace the data and key.
  1917.          */
  1918.         pReplacedEvent = pCurNode->event;
  1919.         pCurNode->key   = key;
  1920.         pCurNode->event = pEvent;
  1921.         }
  1922.     else
  1923.         {
  1924. /* 
  1925.  * Otherwise, we didn't find the key in the table.  Make a new node
  1926.  * and insert it.
  1927.  */
  1928.         pReplacedEvent = NULL;
  1929.         pNewNode = (TN_NODE *) memPartAlloc (pTbl->memPart, sizeof (TN_NODE));
  1930.         if (pNewNode == NULL)
  1931.             return (ERROR);
  1932.         pNewNode->event = pEvent;
  1933.         pNewNode->next  = NULL;
  1934.         pNewNode->key   = key;
  1935.         /* 
  1936.  * pCurNode is always null at this point, pPrevNode->next should be
  1937.  * updated to point to the new node.
  1938.          */
  1939.         pPrevNode->next = pNewNode;
  1940.         }
  1941.     return (OK);
  1942.     }
  1943. /*******************************************************************************
  1944. *
  1945. * tnHashTblIterInit - initialize an iterator key for the taskname hash table
  1946. *
  1947. * This routine intializes and returns the iterKey, used when calling 
  1948. * tnHashTblIterNext, to iterate over all the nodes in the table.  The iterKey
  1949. * is initialized to start interating at the beginning of the table.
  1950. *
  1951. * RETURNS: pointer to a TN_ITER_KEY, or NULL if no memory can be gotten for 
  1952. *          it or for an invalid hash table
  1953. *
  1954. * NOMANUAL
  1955. */
  1956. static TN_ITER_KEY *tnHashTblIterInit 
  1957.     (
  1958.     TN_HASH_TBL *pTbl /* the hash table the key will be used on */
  1959.     )
  1960.     {
  1961.     TN_ITER_KEY *pIterKey;
  1962.     if (pTbl == NULL)
  1963. return (NULL);
  1964.     /*
  1965.      * We should be using a memory region in the main system memory partition
  1966.      * to hold the iterator key, since, if we try to upload a post-mortem log
  1967.      * immediately following a reboot, we don't want to try to allocate new
  1968.      * areas of memory in the post-mortem region which should be left 
  1969.      * untouched until the upload is complete (SPR 21350)
  1970.      */
  1971.     pIterKey = (TN_ITER_KEY *) malloc (sizeof (TN_ITER_KEY));
  1972.     if (pIterKey == NULL)
  1973. return (NULL);
  1974.     pIterKey->index = 0;
  1975.     pIterKey->pCur  = pTbl->tbl[0]->next;
  1976.     return (pIterKey);
  1977.     }
  1978. /*******************************************************************************
  1979. *
  1980. * tnHashTblIterDone - destroy an iterator key for the taskname hash table
  1981. *
  1982. * This routine frees the memory associated with an iterator key that was
  1983. * created with tnHashTblIterInit.
  1984. *
  1985. * RETURNS: N/A
  1986. *
  1987. * NOMANUAL
  1988. */
  1989. static void tnHashTblIterDone
  1990.     (
  1991.     TN_ITER_KEY *pIterKey, /* iter key to destroy */
  1992.     TN_HASH_TBL *pTbl /* hash table the key belongs to */
  1993.     )
  1994.     {
  1995.     if (pTbl != NULL)
  1996.         free ((char *) pIterKey);
  1997.     }
  1998. /*******************************************************************************
  1999. *
  2000. * tnHashTblIterNext - iterator routine, returns next node in hash table
  2001. *
  2002. * This routine is used to access each node stored in the hash table.  A
  2003. * pointer to the node is returned as the value of the function. An iterator
  2004. * key (pIterKey) is passed into and out of this function in order to store
  2005. * where in the table to go next.  
  2006. *
  2007. * The iterator key can be obtained by calling tnHashTblIterInit.
  2008. * The order of nodes returned is from the first to last element in the edge
  2009. * vector of the table (hashTbl->tbl), and then from beginning to end of 
  2010. * each linked list pointed to by the element.
  2011. *
  2012. * RETURN: pointer to next node if one exists, NULL if no more events exist
  2013. *         in the table
  2014. * NOMANUAL
  2015. */
  2016. static TN_NODE *tnHashTblIterNext
  2017.     (
  2018.     TN_HASH_TBL *pTbl,  /* the table to iterate over */
  2019.     TN_ITER_KEY *pIterKey /* iterator key */
  2020.     )
  2021.     {
  2022.     TN_NODE *pRetNode; /* node to return */
  2023.     if (pTbl == NULL)
  2024. return (NULL);
  2025.     /* Find the next node that isn't a dummy header. */
  2026.     pRetNode = NULL;
  2027.     while (pRetNode == NULL && pIterKey->index < pTbl->size)
  2028. {
  2029. if (pIterKey->pCur == NULL)
  2030.     {
  2031.     ++pIterKey->index;
  2032.     pIterKey->pCur = pTbl->tbl [pIterKey->index]->next;
  2033.     }
  2034.         else
  2035.     {
  2036.     pRetNode      = pIterKey->pCur;
  2037.     pIterKey->pCur = pIterKey->pCur->next;
  2038.     }
  2039.         }
  2040.     return (pRetNode);
  2041.     }
  2042. /*******************************************************************************
  2043. *
  2044. * tnHashTblIterNextEvent - returns the next event in the taskname hash table
  2045. *
  2046. * This is exactly like tnHashTblIterNext, but this simply returns the event
  2047. * hanging off the node that tnHashTblIterNext would return.  This keep the
  2048. * data independent of the storage mechanism.
  2049. *
  2050. * RETURN: pointer to next event if one exists, NULL if no more events exist
  2051. *         in the table
  2052. * NOMANUAL
  2053. */
  2054. static TN_EVENT *tnHashTblIterNextEvent
  2055.     (
  2056.     TN_HASH_TBL *pTbl,  /* the table to iterate over */
  2057.     TN_ITER_KEY *pIterKey /* iterator key */
  2058.     )
  2059.     {
  2060.     TN_NODE *pNode;
  2061.     if ((pNode = tnHashTblIterNext (pTbl, pIterKey)) == NULL)
  2062. return (NULL);
  2063.     return (pNode->event); 
  2064.     }
  2065. /*******************************************************************************
  2066. *
  2067. * tnHashTblDestroy - frees all memory associated with a taskname hash table
  2068. *
  2069. * This routine frees all the memory associated with a taskname hash table.
  2070. *
  2071. * RETURNS: OK, or ERROR if invalid hash table
  2072. *
  2073. * INTERNAL:
  2074. * In post mortem mode, it is possible after a reboot, to download a post-mortem
  2075. * log from the user reserved memory area. In this case, the hash table will
  2076. * exist, but vxWorks knowledge of the memory partition will have been lost in
  2077. * the reboot. The old (defunct) part id which is held in the hash table
  2078. * (pTbl->memPart) is invalid as far as vxWorks is concerned and any attempt to
  2079. * memPartFree it will actually result in the area of memory being returned to
  2080. * the system's free pool. This leaves a region of memory in the user reserved
  2081. * area linked into the main system free pool. The system would then attempt to
  2082. * use this for its own purposes.
  2083. * Then, when the user hits GO for a new WindView collection, UITcl will
  2084. * create it's post mortem memory partition to take up the whole of user
  2085. * reserved memory, including the bit that the system has just stolen for itself
  2086. * which will then get overwritten, resulting in a target crash.
  2087. *
  2088. * The solution to this is to define an identifer pmPartId which is set
  2089. * by UITcl when the PM Partition is created. This is in normal memory and will 
  2090. * be zeroed on reboot. This can be used here to detect if the hash table has
  2091. * been memPartAlloc'd during this reboot and can therefore be safely
  2092. * memPartFree'd
  2093. *
  2094. * NOMANUAL
  2095. */
  2096. static STATUS tnHashTblDestroy
  2097.     (
  2098.     TN_HASH_TBL *pTbl  
  2099.     )
  2100.     {
  2101.     int ix; /* counting index */
  2102.     TN_ITER_KEY *pIterKey; /* key to iterate over hash table */
  2103.     TN_NODE *pNode; /* current node to be freed */
  2104.     if (pmPartId != NULL)
  2105. {
  2106.      if (pTbl == NULL)
  2107.     return (ERROR);
  2108.      /*
  2109.       * It is only possible to free the nodes as they are returned from
  2110.       * tnHashTblIterNext, because pIterKey does not refer back to a node
  2111.       * that may be getting freed.  This is only coincidental, but it makes
  2112.       * this routine much simpler.
  2113.       */
  2114.      if ((pIterKey = tnHashTblIterInit (pTbl)) == NULL)
  2115.     return (ERROR);
  2116.      while ((pNode = tnHashTblIterNext (pTbl, pIterKey)) != NULL)
  2117.             {
  2118.     memPartFree (pTbl->memPart, (char *) pNode->event->name);
  2119.     memPartFree (pTbl->memPart, (char *) pNode->event);
  2120.     memPartFree (pTbl->memPart, (char *) pNode);
  2121.     }
  2122.      tnHashTblIterDone (pIterKey, pTbl);
  2123.      /* Free the dummy linked-list headers at each index of the table. */
  2124.      for (ix = 0; ix < pTbl->size; ++ix)
  2125.     memPartFree (pTbl->memPart, (char *) pTbl->tbl [ix]);
  2126.      /* Now free up the edge vector and then the table itself. */
  2127.         memPartFree (pTbl->memPart, (char *) pTbl->tbl);
  2128.         memPartFree (pTbl->memPart, (char *) pTbl);
  2129. }
  2130.     return (OK);
  2131.     }