/**
 * @file tracer.cc
 *
 * Tracer - Classes for creating CAPI trace files.
 *
 * Copyright: 2002-2003 Thomas Wintergerst. All rights reserved.
 *
 * $FreeBSD$
 * $Id: tracer.cc,v 1.1.2.1 2006/02/28 21:35:43 thomas Exp $
 * Project  CAPI for BSD
 * Target   capitrace - Tracing CAPI calls and messages
 * @date    21.09.2002
 * @author  "Thomas Wintergerst" <twinterg@gmx.de>
 * <p>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * </p>
 */

#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");

// System includes
#include <signal.h>
#include <time.h>
#include <iostream>
#include <iomanip>
#include <sstream>
#include <algorithm>
#include <functional>
#include <stdexcept>
#include <errno.h>
#include <capi20.h>
#include <capi_bsd.h>
#include <capi_bsdtrc.h>

// Import includes

#define __TRACER__

// Local includes
#ifndef __TRACE_FILE_HEADER_H
#  include "trace_file_header.h"
#endif
#ifndef __TRACER_H
#  include "tracer.h"
#endif





// === Private definitions ===============================================





/// A function predicate object for comparing a unique tracer number
struct TracerNumberEqual: public std::unary_function<CTracer *, bool>
{
   TracerNumberEqual (int i)
      {
         m_iNumber = i;
      }
   
   bool operator() (const CTracer *pTracer)
      {
         return (pTracer->GetUniqueNumber () == m_iNumber);
      }

   int m_iNumber;
   
}; // TracerNumberEqual



/// A function predicate object for comparing a tracer id
struct TracerIdEqual: public std::unary_function<CTracer *, bool>
{
   TracerIdEqual (u_int32_t dwTracerID)
      {
         m_dwTracerID = dwTracerID;
      }
   
   bool operator() (const CTracer *pTracer)
      {
         return (pTracer->GetTracerID () == m_dwTracerID);
      }
   
   u_int32_t m_dwTracerID;
   
}; // TracerIdEqual



/// A function predicate object for inverse comparing a tracer state
struct TracerStateNotEqual: public std::unary_function<CTracer *, bool>
{
   TracerStateNotEqual (CTracer::State_t state)
      {
         m_state = state;
      }
   
   bool operator() (const CTracer *pTracer)
      {
         return (pTracer->GetState () != m_state);
      }
   
   CTracer::State_t m_state;
   
}; // TracerStateNotEqual





// === Definitions for public declarations ===============================





// === Prototypes of private functions ===================================





/**
 * The signal handler for terminating the CAPI tracer program.
 *
 * This function is called by the operating system if the user presses Ctrl-C or
 * otherwise sends a termination or interrupt signal. The task of this function
 * is to stop all active tracers and tell the CAPI thread to terminate. The
 * termination of the CAPI thread will be done automatically if the thread
 * receives the stop confirmation for the last active tracer.
 *
 * @param int                   Unused: The type of signal received.
 *
 * @return Nothing
 */
void TermSignalHandler (int);

/**
 * The thread function for handling incoming CAPI messages.
 *
 + @param pArg                  I: The address of the corresponding CTracerList
 *                                 object.
 *
 * @return Allways NULL.
 */
void *DoCapiService
   (void *pArg);





// === Implementation of class members ===================================





// --- Class for creating a single CAPI trace ----------------------------

/// The class global counter for creating unique tracer numbers.
int CTracer::m_iCurrUniqueNumber = 0;





/**
 * Constructor.
 */

CTracer::CTracer (const CConfig::TracerConfig_t &roTracerConfig)
{
   // assume all tracer objects are created by the same thread one after the
   // other
   m_iUniqueNumber = ++m_iCurrUniqueNumber;
   
   m_state      = STATE_IDLE;
   m_dwTracerID = 0;
   m_oConfig    = roTracerConfig;
   
   // if the tracer file name is an empty string or "-", the trace output shall
   // go to stdout
   if (roTracerConfig.strTraceFile == "" ||
       roTracerConfig.strTraceFile == "-")
   {
      m_poOutFStream = NULL;
      m_poOS = &(std::cout);
   }
   // else we must create a new stream object to create a new regular file
   else
   {
      m_poOutFStream = new std::ofstream (roTracerConfig.strTraceFile.c_str (),
                                          std::ios::out | std::ios::trunc |
                                             std::ios::binary);
      if (! m_poOutFStream->is_open ())
      {
         std::ostringstream oss;
         oss << "Unable to create output file \""
             << roTracerConfig.strTraceFile << "\"";
         throw std::runtime_error (oss.str ().c_str ());
      }
      m_poOS = m_poOutFStream;
   }
   
   // now the target output stream is open write the file header to the stream
   TraceFileHeader_t fh;
   struct timeval    tv;
   memset (&fh, 0, sizeof (fh));
   C_PUT_DWORD (fh.dwMagic, TRACE_FILE_HEADER_MAGIC);
   if (m_oConfig.fIsCtlrTracer)
   {
      C_PUT_DWORD (fh.dwCtlrNum, m_oConfig.uCtlrNum);
   }
   else
   {
      C_PUT_DWORD (fh.dwCtlrNum, 0);
   }
   C_PUT_BYTE (fh.bIncludeFctCalls,     m_oConfig.fIncludeFctCalls);
   C_PUT_BYTE (fh.bIncludeMessages,     m_oConfig.fIncludeMessages);
   C_PUT_BYTE (fh.bIncludeDataB3Msgs,   m_oConfig.fIncludeDataB3Msgs);
   C_PUT_BYTE (fh.bIncludeDataB3Blocks, m_oConfig.fIncludeDataB3Blocks);
   C_PUT_WORD (fh.wDataB3BlockLen,      m_oConfig.nDataB3BlockLen);
   if (gettimeofday (&tv, NULL) == 0)
   {
      C_PUT_DWORD (fh.dwTimeSeconds, tv.tv_sec);
      C_PUT_DWORD (fh.dwTimeMicroSec, tv.tv_usec);
   }
   m_poOS->write ((char *) &fh, sizeof (fh));
   
} // CTracer::CTracer





/**
 * Destructor.
 */

CTracer::~CTracer (void)
{
   m_poOS = NULL;
   if (m_poOutFStream != NULL)
   {
      if (m_poOutFStream->is_open ())
      {
         m_poOutFStream->close ();
      }
      delete m_poOutFStream;
      m_poOutFStream = NULL;
   }
   
} // CTracer::~CTracer





/**
 * Query the state of this tracer object.
 */

CTracer::State_t CTracer::GetState (void) const
{
   return (m_state);
} // CTracer::GetState





/**
 * Query the unique number of the current tracer object.
 */

int CTracer::GetUniqueNumber (void) const
{
   return (m_iUniqueNumber);
} // CTracer::GetUniqueNumber





/**
 * Query if the tracer is of type controller tracer.
 */

bool CTracer::IsCtlrTracer (void) const
{
   return (m_oConfig.fIsCtlrTracer);
} // CTracer::IsCtlrTracer





/**
 * Query for the controller number this (controller) tracer runs for.
 *
 * @note If this is an application tracer, the result value of this
 *       function is simple null, i.e. an invalid controller number.
 */

unsigned CTracer::GetCtlr (void) const
{
   return (m_oConfig.fIsCtlrTracer ? m_oConfig.uCtlrNum : 0);
} // CTracer::GetCtlr





/**
 * Query the tracer id for the running tracer.
 *
 * @note If the tracer is not currently running, the result is simply
 *       null, i.e. an invalid tracer id.
 */

u_int32_t CTracer::GetTracerID (void) const
{
   return (m_dwTracerID);
} // CTracer::GetTracerID





/**
 * Send CAPI message to start the tracer.
 *
 * @attention To serialize CAPI access this message must be called with
 *            the CAPI mutex locked.
 *
 * @param uCapiApplID           I: The CAPI application id to use.
 *
 * @return                      The CAPI result value of the
 *                              capi20_put_message() function. If it is CAPI_OK,
 *                              the message was successfully sent, the tracer
 *                              may go to active state if a positive
 *                              confirmation is received later. Else the message
 *                              could not be sent, the tracer will not go to
 *                              active state.
 */

unsigned CTracer::SendStartTracerRequest
   (unsigned uCapiApplID)
{
   CAPIMsg_t       capiMsg;
   CAPITraceReq_t *pTraceReq;
   u_int32_t       dwTraceMask;
   
   C_PUT_WORD (capiMsg.head.wApp, uCapiApplID);
   C_PUT_WORD (capiMsg.head.wCmd, CAPI_REQUEST (C_MANUFACTURER));
   C_PUT_WORD (capiMsg.head.wNum, (u_int16_t) m_iUniqueNumber);
   if (m_oConfig.fIsCtlrTracer)
   {
      C_PUT_DWORD (capiMsg.head.dwCid, m_oConfig.uCtlrNum);
   }
   else
   {
      C_PUT_DWORD (capiMsg.head.dwCid, 0);
   }
   C_PUT_DWORD (capiMsg.info.manufacturer_req.dwManuID, C4B_TRACE_MANU_ID);
   pTraceReq = (CAPITraceReq_t *)
                  ((u_int8_t *) &(capiMsg.info.manufacturer_req.dwManuID) +
                   sizeof (capiMsg.info.manufacturer_req.dwManuID));
   dwTraceMask = 0;
   if (m_oConfig.fIncludeFctCalls)
   {
      dwTraceMask |= C4BTRC_TRCMSK_FCT_CALLS;
   }
   if (m_oConfig.fIncludeMessages)
   {
      dwTraceMask |= C4BTRC_TRCMSK_MSGS;
   }
   if (m_oConfig.fIncludeDataB3Msgs)
   {
      dwTraceMask |= C4BTRC_TRCMSK_DATA_B3_MSGS;
   }
   if (m_oConfig.fIncludeDataB3Blocks)
   {
      dwTraceMask |= C4BTRC_TRCMSK_DATA_BLOCKS;
   }
   if (m_oConfig.fIsCtlrTracer)
   {
      C_PUT_WORD (pTraceReq->wRequest, C4BTRC_REQ_START_CTLR_TRACER);
      C_PUT_WORD (pTraceReq->info.start_ctlr_tracer.wLength,
                  sizeof (pTraceReq->info.start_ctlr_tracer));
      C_PUT_DWORD (pTraceReq->info.start_ctlr_tracer.dwTraceMask, dwTraceMask);
      C_PUT_DWORD (pTraceReq->info.start_ctlr_tracer.dwDataBlockLength,
                   m_oConfig.nDataB3BlockLen);
      C_PUT_DWORD (pTraceReq->info.start_ctlr_tracer.dwCtlrNum,
                   m_oConfig.uCtlrNum);
      C_PUT_WORD (capiMsg.head.wLen,
                  sizeof (capiMsg.head) +
                     sizeof (capiMsg.info.manufacturer_req) +
                     sizeof (pTraceReq->wRequest) +
                     sizeof (pTraceReq->info.start_ctlr_tracer));
   }
   else
   {
      C_PUT_WORD (pTraceReq->wRequest, C4BTRC_REQ_START_APPL_TRACER);
      C_PUT_WORD (pTraceReq->info.start_appl_tracer.wLength,
                  sizeof (pTraceReq->info.start_appl_tracer));
      C_PUT_DWORD (pTraceReq->info.start_appl_tracer.dwTraceMask, dwTraceMask);
      C_PUT_DWORD (pTraceReq->info.start_appl_tracer.dwDataBlockLength,
                   m_oConfig.nDataB3BlockLen);
      C_PUT_WORD (capiMsg.head.wLen,
                  sizeof (capiMsg.head) +
                     sizeof (capiMsg.info.manufacturer_req) +
                     sizeof (pTraceReq->wRequest) +
                     sizeof (pTraceReq->info.start_appl_tracer));
   }
   
   unsigned        uRes;
   unsigned        uTimeout;
   struct timespec ts;
   uTimeout = (unsigned) time (NULL) + 5;
   ts.tv_sec = 0;
   ts.tv_nsec = 500000000;      // half a second
   do
   {
      uRes = capi20_put_message (uCapiApplID, &capiMsg);
      if (uRes == CME_PUT_QUEUE_FULL || uRes == CME_BUSY)
      {
         (void) nanosleep (&ts, NULL);
      }
   } while ((uRes == CME_PUT_QUEUE_FULL ||
             uRes == CME_BUSY) &&
            uTimeout >= (unsigned) time (NULL));

   if (uRes == CAPI_OK)
   {
      m_state = STATE_STARTING;
   }
   else
   {
      m_dwTracerID = 0;
      m_state = STATE_STOPPED;
   }
   
   return (uRes);
} // CTracer::SendStartTracerRequest





/**
 * Send CAPI message to stop the tracer.
 *
 * @attention To serialize CAPI access this message must be called with
 *            the CAPI mutex locked.
 *
 * @param uCapiApplID           I: The CAPI application id to use.
 *
 * @return                      The CAPI result value of the
 *                              capi20_put_message() function call. If it is
 *                              CAPI_OK, the message was successfully sent, the
 *                              tracer may go to disabled state if a positive
 *                              confirmation is received later. Else the message
 *                              could not be sent, the tracer will not go to
 *                              disabled state.
 */

unsigned CTracer::SendStopTracerRequest
   (unsigned uCapiApplID)
{
   CAPIMsg_t       capiMsg;
   CAPITraceReq_t *pTraceReq;
   
   if (m_state != STATE_ACTIVE)
   {
      m_dwTracerID = 0;
      m_state = STATE_STOPPED;
      return (CAPI_OK);
   }
   
   C_PUT_WORD (capiMsg.head.wApp, uCapiApplID);
   C_PUT_WORD (capiMsg.head.wCmd, CAPI_REQUEST (C_MANUFACTURER));
   C_PUT_WORD (capiMsg.head.wNum, (u_int16_t) m_iUniqueNumber);
   if (m_oConfig.fIsCtlrTracer)
   {
      C_PUT_DWORD (capiMsg.head.dwCid, m_oConfig.uCtlrNum);
   }
   else
   {
      C_PUT_DWORD (capiMsg.head.dwCid, 0);
   }
   C_PUT_DWORD (capiMsg.info.manufacturer_req.dwManuID, C4B_TRACE_MANU_ID);
   pTraceReq = (CAPITraceReq_t *)
                  ((u_int8_t *) &(capiMsg.info.manufacturer_req.dwManuID) +
                   sizeof (capiMsg.info.manufacturer_req.dwManuID));
   C_PUT_WORD (pTraceReq->wRequest, C4BTRC_REQ_STOP_TRACER);
   C_PUT_WORD (pTraceReq->info.stop_tracer.wLength,
               sizeof (pTraceReq->info.stop_tracer));
   C_PUT_DWORD (pTraceReq->info.stop_tracer.dwTracerID, m_dwTracerID);
   C_PUT_WORD (capiMsg.head.wLen,
               sizeof (capiMsg.head) +
                  sizeof (capiMsg.info.manufacturer_req) +
                  sizeof (pTraceReq->wRequest) +
                  sizeof (pTraceReq->info.stop_tracer));
   
   unsigned        uRes;
   unsigned        uTimeout;
   struct timespec ts;
   uTimeout = (unsigned) time (NULL) + 5;
   ts.tv_sec = 0;
   ts.tv_nsec = 500000000;      // half a second
   do
   {
      uRes = capi20_put_message (uCapiApplID, &capiMsg);
      if (uRes == CME_PUT_QUEUE_FULL || uRes == CME_BUSY)
      {
         (void) nanosleep (&ts, NULL);
      }
   } while ((uRes == CME_PUT_QUEUE_FULL ||
             uRes == CME_BUSY) &&
            uTimeout >= (unsigned) time (NULL));

   if (uRes == CAPI_OK)
   {
      m_state = STATE_STOPPING;
   }
   else
   {
      m_state = STATE_STOPPED;
   }
   
   return (uRes);
} // CTracer::SendStopTracerRequest





/**
 * Handle a confirmation for a tracer start request.
 *
 * @param pMsg            I: The CAPI message for the start confirmation.
 *
 * @return Nothing.
 */

void CTracer::HandleStartTracerConfirm
   (const CAPIMsg_t *pMsg)
{
   CAPITraceConf_t *pTraceConf;
   u_int16_t        wInfo;
   
   // assume the message is already verified to be valid and to contain our
   // unique number --> check the info value and extract the tracer id
   
   pTraceConf = (CAPITraceConf_t *)
                   ((u_int8_t *) &(pMsg->info.manufacturer_ind.dwManuID) +
                    sizeof (pMsg->info.manufacturer_ind.dwManuID));
   wInfo = C_GET_WORD (pTraceConf->info.start_tracer.wInfo);
   if (wInfo != CAPI_OK)
   {
      std::cerr << "Error 0x"
		<< std::hex << std::setw (4) << std::setfill ('0') << wInfo
		<< std::dec << std::setfill (' ')
                << " in confirmation to start ";
      if (m_oConfig.fIsCtlrTracer)
      {
         std::cerr << "controller tracer for controller "
                   << m_oConfig.uCtlrNum << std::endl;
      }
      else
      {
         std::cerr << "application tracer" << std::endl;
      }
      m_state = STATE_STOPPED;
      return;
   }
   
   m_dwTracerID = C_GET_DWORD (pTraceConf->info.start_tracer.dwTracerID);
   m_state = STATE_ACTIVE;
   
} // CTracer::HandleStartTracerConfirm





/**
 * Handle a confirmation for a tracer stop request.
 *
 * @param pMsg            I: The CAPI message for the stop confirmation.
 *
 * @return Nothing.
 */

void CTracer::HandleStopTracerConfirm
   (const CAPIMsg_t *pMsg)
{
   CAPITraceConf_t *pTraceConf;
   u_int16_t        wInfo;
   
   // assume the message is already verified to be valid and to contain our
   // unique number --> only check the info value in case of an error
   
   pTraceConf = (CAPITraceConf_t *)
                   ((u_int8_t *) &(pMsg->info.manufacturer_ind.dwManuID) +
                    sizeof (pMsg->info.manufacturer_ind.dwManuID));
   wInfo = C_GET_WORD (pTraceConf->info.stop_tracer.wInfo);
   if (wInfo != CAPI_OK)
   {
      std::cerr << "Error 0x"
		<< std::hex << std::setw (4) << std::setfill ('0') << wInfo
		<< std::dec << std::setfill (' ')
                << " in confirmation to stop ";
      if (m_oConfig.fIsCtlrTracer)
      {
         std::cerr << "controller tracer for controller "
                   << m_oConfig.uCtlrNum << std::endl;
      }
      else
      {
         std::cerr << "application tracer" << std::endl;
      }
   }
   
   // write a last trace record to the output stream only consisting of the
   // trace message header and an invalid trace message type; this will be
   // interpreted as the timestamp for tracer termination
   CAPITraceMsgHeader_t traceHeader;
   struct timeval       tv;
   memset (&traceHeader, 0, sizeof (traceHeader));
   C_PUT_WORD (traceHeader.wLength, sizeof (traceHeader));
   C_PUT_DWORD (traceHeader.dwTracerID, m_dwTracerID);
   if (gettimeofday (&tv, NULL) == 0)
   {
      C_PUT_DWORD (traceHeader.dwTimeSeconds, tv.tv_sec);
      C_PUT_DWORD (traceHeader.dwTimeMicroSec, tv.tv_usec);
   }
   C_PUT_WORD (traceHeader.wMsgType, 0);
   m_poOS->write ((char *) &traceHeader, sizeof (traceHeader));
   
   m_dwTracerID = 0;
   m_state = STATE_STOPPED;
   
} // CTracer::HandleStopTracerConfirm





/**
 * Handle a CAPI trace message.
 *
 * @param pTraceMsg       I: The trace message information part of the
 *                           CAPI message after the dwManuID part.
 *
 * @return Nothing.
 */

void CTracer::HandleTraceMessage
   (const CAPITraceMsg_t *pTraceMsg)
{
   size_t    nMsgLen;
   size_t    nDataLen;
   u_int8_t *pData;
   unsigned  uMsgType;
   
   if (m_poOS->fail ())
   {
      // an earlier write request failed, do not attempt to write on
      return;
   }
   
   // first simply write trace message itself to the output stream
   nMsgLen = (size_t) C_GET_WORD (pTraceMsg->head.wLength);
   m_poOS->write ((char *) pTraceMsg, nMsgLen);
   if (m_poOS->fail ())
   {
      std::cerr << "Error writing " << nMsgLen
                << " message bytes to output stream for ";
      if (m_oConfig.fIsCtlrTracer)
      {
         std::cerr << "controller tracer for controller "
                   << m_oConfig.uCtlrNum << std::endl;
      }
      else
      {
         std::cerr << "application tracer" << std::endl;
      }
      return;
   }
   
   // if the message contains an attached data block, now write this block out
   uMsgType = (unsigned) C_GET_WORD (pTraceMsg->head.wMsgType);
   if (uMsgType == C4BTRC_MSGTYPE_APPL_MSG)
   {
#if defined(__alpha__) || defined (__ia64__) || defined (__amd64__) || defined (__sparc64__)
      pData = (u_int8_t *)
                 C_MAKE_QWORD
                    (C_GET_DWORD (pTraceMsg->info.appl_msg.dwDataPointerLow),
                     C_GET_DWORD (pTraceMsg->info.appl_msg.dwDataPointerHigh));
#else /* __alpha__ || __ia64__ || __amd64__ || __sparc64__ */
      pData = (u_int8_t *)
                 C_GET_DWORD (pTraceMsg->info.appl_msg.dwDataPointerLow);
#endif /* __alpha__ || __ia64__ || __amd64__ || __sparc64__ */
      nDataLen = (size_t) C_GET_DWORD (pTraceMsg->info.appl_msg.dwDataLength);
   }
   else if (uMsgType == C4BTRC_MSGTYPE_DRVR_MSG)
   {
#if defined(__alpha__) || defined (__ia64__) || defined (__amd64__) || defined (__sparc64__)
      pData = (u_int8_t *)
                 C_MAKE_QWORD
                    (C_GET_DWORD (pTraceMsg->info.drvr_msg.dwDataPointerLow),
                     C_GET_DWORD (pTraceMsg->info.drvr_msg.dwDataPointerHigh));
#else /* __alpha__ || __ia64__ || __amd64__ || __sparc64__ */
      pData = (u_int8_t *) C_GET_DWORD (pTraceMsg->info.drvr_msg.dwDataPointerLow);
#endif /* __alpha__ || __ia64__ || __amd64__ || __sparc64__ */
      nDataLen = (size_t) C_GET_DWORD (pTraceMsg->info.drvr_msg.dwDataLength);
   }
   else
   {
      pData = NULL;
      nDataLen = 0;
   }
   if (pData && nDataLen > 0)
   {
      m_poOS->write ((char *) pData, nDataLen);
      if (m_poOS->fail ())
      {
         std::cerr << "Error writing " << nDataLen
                   << " data bytes to output stream for ";
         if (m_oConfig.fIsCtlrTracer)
         {
            std::cerr << "controller tracer for controller "
                      << m_oConfig.uCtlrNum << std::endl;
         }
         else
         {
            std::cerr << "application tracer" << std::endl;
         }
         return;
      }
   }
   
} // CTracer::HandleTraceMessage





// --- Class for maintaining a list of CAPI tracers ----------------------

/// Place to store the address of the one-and-only instance of this class.
CTracerList *CTracerList::m_poInstance = NULL;





/**
 * Constructor.
 */
CTracerList::CTracerList (void)
{
   int iRes;
   
   // check if there is already a running instance of this class
   if (m_poInstance)
   {
      throw std::runtime_error
               ("Attempt to create more than one instance of CTracerList");
   }
   m_poInstance = this;
   
   // initialize member variables
   m_fCapiRegistered    = false;
   m_uCapiApplID        = 0;
   m_fCapiThreadRunning = false;
   m_capiThreadID       = 0;
   
   // initialize the mutex to serialize CAPI access
   iRes = pthread_mutex_init (&m_mutexCapi, NULL);
   if (iRes != 0)
   {
      std::ostringstream oss;
      oss << "Error initializing CAPI serialization mutex: "
          << strerror (errno) << " (" << errno << ")";
      throw std::runtime_error (oss.str ().c_str ());
   }
   m_fCapiMutexValid = true;
   
} // CTracerList::CTracerList





/**
 * Destructor.
 */
CTracerList::~CTracerList (void)
{
   const_iterator iter;
   
   // stop all tracers and the CAPI thread if still running
   Stop ();
   
   // now unregister at CAPI
   if (m_fCapiRegistered)
   {
      m_fCapiRegistered = false;
      (void) capi20_release (m_uCapiApplID);
   }
   
   // destroy the CAPI mutex as it is not needed any more
   if (m_fCapiMutexValid)
   {
      m_fCapiMutexValid = false;
      (void) pthread_mutex_destroy (&m_mutexCapi);
   }
   
   // cleanup requires to release memory for all tracer objects in the list
   for (iter = begin (); iter != end (); ++iter)
   {
      delete *iter;
   }
   
   // restore default signal handlers so  future signals are not sent to a
   // non-existing object
   struct sigaction sigact;
   memset (&sigact, 0, sizeof (sigact));
   sigact.sa_handler = SIG_DFL;
   (void) sigaction (SIGTERM, &sigact, NULL);
   (void) sigaction (SIGINT, &sigact, NULL);
   
   // now there is no instance of the tracer list any more
   m_poInstance = NULL;
   
} // CTracerList::~CTracerList





/**
 * Get the running instance of this class.
 */

CTracerList *CTracerList::GetInstance (void)
{
   if (! m_poInstance)
   {
      throw std::runtime_error ("No instance of CTracerList running");
   }
   return (m_poInstance);
} // CTracerList::GetInstance





/**
 * Add a new tracer object to this list.
 */

void CTracerList::Add
   (const CConfig::TracerConfig_t &roTracerConfig)
{
   // for now simply create and add the new tracer to the list
   push_back (new CTracer (roTracerConfig));
   
} // CTracerList::Add





/**
 * Start all tracers and handle CAPI messages until program termination
 * signal.
 */

void CTracerList::Run (void)
{
   int      iRes;
   unsigned uRes;
   
   // first check if there is at least one tracer on our list
   if (size () == 0)
   {
      std::cerr << "No tracer specified to start" << std::endl;
      return;
   }
   
   // register the signal handler for the termination signal; this is used later
   // as a trigger to stop all tracers and to terminate program execution
   // thereafter
   struct sigaction sigact;
   memset (&sigact, 0, sizeof (sigact));
   sigact.sa_handler = TermSignalHandler;
   sigact.sa_flags = SA_RESTART;
   (void) sigaction (SIGTERM, &sigact, NULL);
   (void) sigaction (SIGINT, &sigact, NULL);
   
   // perform CAPI registration
   if (! m_fCapiRegistered)
   {
      uRes = capi20_register (0, 7, 2048, &m_uCapiApplID);
      if (uRes != CAPI_OK)
      {
         std::ostringstream oss;
         oss << "Error 0x"
	     << std::hex << std::setw (4) << std::setfill ('0') << uRes
	     << std::dec << std::setfill (' ')
	     << " in CAPI registration";
         throw std::runtime_error (oss.str ().c_str ());
      }
      m_fCapiRegistered = true;
   }
   
   // start the CAPI thread for handling incoming CAPI messages
   if (! m_fCapiThreadRunning)
   {
      iRes = pthread_create (&m_capiThreadID, NULL, DoCapiService, this);
      if (iRes != 0)
      {
         std::ostringstream oss;
         oss << "Unable to start CAPI thread: " << strerror (errno)
             << " (" << errno << ")";
         throw std::runtime_error (oss.str ().c_str ());
      }
      m_fCapiThreadRunning = true;
   }
   
   // now start all CAPI tracers; we don't have to wait for confirmations here,
   // because the CAPI thread will handle them
   iterator iter;
   for (iter = begin (); iter != end (); ++iter)
   {
      if ((*iter)->GetState () == CTracer::STATE_IDLE)
      {
         (void) pthread_mutex_lock (&m_mutexCapi);
         try
         {
            uRes = (*iter)->SendStartTracerRequest (m_uCapiApplID);
         }
         catch (...)
         {
            (void) pthread_mutex_unlock (&m_mutexCapi);
            throw;
         }
         (void) pthread_mutex_unlock (&m_mutexCapi);
         if (uRes != CAPI_OK)
         {
            std::ostringstream oss;
            oss << "Error 0x"
		<< std::hex << std::setw (4) << std::setfill ('0') << uRes
		<< std::dec << std::setfill (' ')
		<< " starting ";
            if ((*iter)->IsCtlrTracer ())
            {
               oss << "controller tracer for controller "
                   << (*iter)->GetCtlr ();
            }
            else
            {
               oss << "application tracer";
            }
            throw std::runtime_error (oss.str ().c_str ());
         }
      }
   }
   
   // all tracers running, wait for termination signal to trigger stopping the
   // tracers and terminating the CAPI thread
   std::cout << "Tracers running, press Ctrl-C to terminate..." << std::endl;
   (void) pthread_join (m_capiThreadID, NULL);
   std::cout << "Tracers stopped" << std::endl;
   
} // CTracerList::Run





/**
 * Stop all running tracers and the handling of incoming CAPI messages.
 */

void CTracerList::Stop (void)
{
   iterator iter;
   unsigned uRes;

   // stop all tracers that are currently running
   for (iter = begin (); iter != end (); ++iter)
   {
      if ((*iter)->GetState () != CTracer::STATE_STOPPED)
      {
         (void) pthread_mutex_lock (&m_mutexCapi);
         try
         {
            uRes = (*iter)->SendStopTracerRequest (m_uCapiApplID);
         }
         catch (...)
         {
            (void) pthread_mutex_unlock (&m_mutexCapi);
            throw;
         }
         (void) pthread_mutex_unlock (&m_mutexCapi);
         if (uRes != CAPI_OK)
         {
            std::cerr << "Error " << uRes << " stopping ";
            if ((*iter)->IsCtlrTracer ())
            {
               std::cerr << "controller tracer for controller "
                         << (*iter)->GetCtlr ();
            }
            else
            {
               std::cerr << "application tracer";
            }
            std::cerr << std::endl;
            // we must go on even on error
         }
      }
   }
   
   // The CAPI thread will automatically stop if it gets a CAPI confirmation for
   // a stopped tracer and there is no other tracer active any more. To wait for
   // the CAPI thread to terminate is the task of the main Run() member
   // function.
   
} // CTracerList::Stop





// === Implementation of public functions ================================





// === Implementation of private functions ===============================





/**
 * The signal handler for terminating the CAPI tracer program.
 *
 * This function is called by the operating system if the user presses Ctrl-C or
 * otherwise sends a termination or interrupt signal. The task of this function
 * is to stop all active tracers and tell the CAPI thread to terminate. The
 * termination of the CAPI thread will be done automatically if the thread
 * receives the stop confirmation for the last active tracer.
 *
 * @param int                   Unused: The type of signal received.
 *
 * @return Nothing
 */
void TermSignalHandler (int)
{
   CTracerList::GetInstance ()->Stop ();
   
} // TermSignalHandler





/**
 * The thread function for handling incoming CAPI messages.
 *
 + @param pArg                  I: The address of the corresponding CTracerList
 *                                 object.
 *
 * @return Allways NULL.
 */
void *DoCapiService
   (void *pArg)
{
   CTracerList           *pThis = (CTracerList *) pArg;
   CAPIMsg_t             *pMsg;
   u_int8_t              *p;
   CAPITraceConf_t       *pTraceConf;
   CAPITraceMsg_t        *pTraceInd;
   CTracerList::iterator  iter;
   u_int32_t              dwTracerID;
   unsigned               uRes;
   
   while (CTracerList::m_poInstance && pThis->m_fCapiRegistered)
   {
      (void) capi20_wait_for_message (pThis->m_uCapiApplID, NULL);
      if (! CTracerList::m_poInstance || ! pThis->m_fCapiRegistered)
      {
         break;
      }
      
      (void) pthread_mutex_lock (&(pThis->m_mutexCapi));
      uRes = capi20_get_message (pThis->m_uCapiApplID, &pMsg);
      (void) pthread_mutex_unlock (&(pThis->m_mutexCapi));
      if (uRes == CME_GET_QUEUE_EMPTY)
      {
         continue;
      }
      if (uRes != CAPI_OK)
      {
         std::cerr << "Error 0x"
		   << std::hex << std::setw (4) << std::setfill ('0') << uRes
		   << std::dec << std::setfill (' ')
                   << " while getting CAPI message" << std::endl;
         continue;
      }
      
      // now there is a valid CAPI message, determine its type
      
      // assume the message is a Manufacturer-Indication for C4B tracing; will
      // be checked later
      // Note: Every manufacturer message has the same basic layout, i.e. it
      //       starts with the dwManuID and continues with the trace message
      //       part.
      p = (u_int8_t *) &(pMsg->info.manufacturer_ind.dwManuID) +
          sizeof (pMsg->info.manufacturer_ind.dwManuID);
      pTraceConf = (CAPITraceConf_t *) p;
      pTraceInd  = (CAPITraceMsg_t *) p;

      // check for a start tracer confirmation
      if (CAPI_GET_CMD (pMsg) == CAPI_CONFIRM (C_MANUFACTURER) &&
          pMsg->info.manufacturer_conf.dwManuID == C4B_TRACE_MANU_ID &&
          (pTraceConf->wRequest == C4BTRC_REQ_START_APPL_TRACER ||
           pTraceConf->wRequest == C4BTRC_REQ_START_CTLR_TRACER))
      {
         // the tracer object is expected to send the start message with an
         // internal unique number, so the tracer object is determined by the
         // message number
         iter = find_if (pThis->begin (), pThis->end (),
                         TracerNumberEqual ((int) CAPI_GET_MSGNUM (pMsg)));
         if (iter == pThis->end ())
         {
            std::cerr << "Got unknown tracer number " << CAPI_GET_MSGNUM (pMsg)
                      << " in start tracer confirmation" << std::endl;
         }
         else
         {
            // so iter points to a valid tracer, forward the message to it
            (*iter)->HandleStartTracerConfirm (pMsg);
         }
      }
      // check for a stop tracer confirmation
      else if (CAPI_GET_CMD (pMsg) == CAPI_CONFIRM (C_MANUFACTURER) &&
               pMsg->info.manufacturer_conf.dwManuID == C4B_TRACE_MANU_ID &&
               pTraceConf->wRequest == C4BTRC_REQ_STOP_TRACER)
      {
         // determine the tracer id
         dwTracerID = C_GET_DWORD (pTraceConf->info.stop_tracer.dwTracerID);
         
         // determine the tracer object with the tracer id
         iter = find_if (pThis->begin (), pThis->end (),
                         TracerIdEqual (dwTracerID));
         if (iter == pThis->end ())
         {
            std::cerr << "Got unknown tracer number " << CAPI_GET_MSGNUM (pMsg)
                      << " in stop tracer confirmation" << std::endl;
         }
         else
         {
            // so iter points to a valid tracer, forward the message to it
            (*iter)->HandleStopTracerConfirm (pMsg);
         }
      }
      // check for a trace message
      else if (CAPI_GET_CMD (pMsg) == CAPI_INDICAT (C_MANUFACTURER) &&
               pMsg->info.manufacturer_conf.dwManuID == C4B_TRACE_MANU_ID)
      {
         // determine the tracer id
         dwTracerID = C_GET_DWORD (pTraceInd->head.dwTracerID);
         
         // determine the tracer object with the tracer id
         iter = find_if (pThis->begin (), pThis->end (),
                         TracerIdEqual (dwTracerID));
         if (iter == pThis->end ())
         {
            std::cerr << "Got trace message with unknown tracer id "
                      << dwTracerID << std::endl;
         }
         else
         {
            // forward the trace message to the tracer object
            (*iter)->HandleTraceMessage (pTraceInd);
         }
      }
      else
      {
         std::cerr << "Got unexpected CAPI message, command 0x"
                   << std::hex << std::setw (4) << std::setfill ('0')
		   << CAPI_GET_CMD (pMsg) << std::dec << std::setfill (' ')
		   << std::endl;
      }
      
      // check for all tracers in stopped state for leaving the message loop
      iter = find_if (pThis->begin (), pThis->end (),
                      TracerStateNotEqual (CTracer::STATE_STOPPED));
      if (iter == pThis->end ())
      {
         // could not find any tracer with a state other than stopped --> ready
         break;
      }
   }
   
   return (NULL);
} // DoCapiService
