/*
** Copyright (C) 2005 Sourcefire, Inc.
** Author: Steven Sturges <ssturges@sourcefire.com>
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License Version 2 as
** published by the Free Software Foundation.  You may not use, modify or
** distribute this program under any other version of the GNU General
** Public License.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/

/* $Id$ */

#ifndef __PROFILER_H__
#define __PROFILER_H__

#ifdef PERF_PROFILING
#ifndef UINT64
#define UINT64 unsigned long long
#endif

/* Assembly to find clock ticks.  Intel only */
#ifdef WIN32
#define get_clockticks(val) \
    QueryPerformanceCounter((PLARGE_INTEGER)&val)
#else
#if (defined(__i386) || defined(__ia64) || defined(__amd64) )
#define get_clockticks(val) \
{ \
    u_int32_t a, d; \
    __asm__ __volatile__ ("rdtsc" : "=a" (a), "=d" (d));  \
    val = ((UINT64)a) | (((UINT64)d) << 32);  \
}
#else
#if (defined(__GNUC__) && (defined(__powerpc__) || (defined(__ppc__))))
#define get_clockticks(val) \
{ \
    u_int32_t tbu0, tbu1, tbl; \
    do \
    { \
        __asm__ __volatile__ ("mftbu %0" : "=r"(tbu0)); \
        __asm__ __volatile__ ("mftb %0" : "=r"(tbl)); \
        __asm__ __volatile__ ("mftbu %0" : "=r"(tbu1)); \
    } while (tbu0 != tbu1); \
    val = ((UINT64)tbl) | (((UINT64)tbu0) << 32);  \
}
#else
#define get_clockticks(val)
#endif /* POWERPC || PPC */
#endif /* I386 || IA64 || AMD64 */
#endif /* WIN32 */

/* Sort preferences for rule profiling */
#define PROFILE_SORT_CHECKS 1
#define PROFILE_SORT_MATCHES 2
#define PROFILE_SORT_NOMATCHES 3
#define PROFILE_SORT_AVG_TICKS 4
#define PROFILE_SORT_AVG_TICKS_PER_MATCH 5
#define PROFILE_SORT_AVG_TICKS_PER_NOMATCH 6
#define PROFILE_SORT_TOTAL_TICKS 7

/* MACROS that handle profiling of rules and preprocessors */
#define PROFILE_VARS UINT64 ticks_start = 0, ticks_end = 0, ticks_delta

#define PROFILE_START \
    get_clockticks(ticks_start);

#define PROFILE_END \
    get_clockticks(ticks_end); \
    ticks_delta = ticks_end - ticks_start;

#ifndef PROFILING_RULES
#define PROFILING_RULES pv.profile_rules_flag
#endif

#define OTN_PROFILE_START(otn) \
    if (PROFILING_RULES) { \
        otn->checks++; \
        PROFILE_START; \
    }

#define OTN_PROFILE_END_MATCH(otn) \
    if (PROFILING_RULES) { \
        PROFILE_END; \
        otn->ticks += ticks_delta; \
        otn->ticks_match += ticks_delta; \
        otn->matches++; \
    }

#define OTN_PROFILE_NOALERT(otn) \
    if (PROFILING_RULES) { \
        otn->noalerts=1; \
    }

#define OTN_PROFILE_END_NOMATCH(otn) \
    if (PROFILING_RULES) { \
        PROFILE_END; \
        otn->ticks += ticks_delta; \
        otn->ticks_no_match += ticks_delta; \
    }
#define OTN_PROFILE_ALERT(otn) otn->alerts++;

#ifndef PROFILING_PREPROCS
#define PROFILING_PREPROCS pv.profile_preprocs_flag
#endif

#define PREPROC_PROFILE_START(ppstat) \
    if (PROFILING_PREPROCS) { \
        ppstat.checks++; \
        PROFILE_START; \
        ppstat.ticks_start = ticks_start; \
    } 

#define PREPROC_PROFILE_REENTER_START(ppstat) \
    if (PROFILING_PREPROCS) { \
        PROFILE_START; \
        ppstat.ticks_start = ticks_start; \
    } 

#define PREPROC_PROFILE_TMPSTART(ppstat) \
    if (PROFILING_PREPROCS) { \
        PROFILE_START; \
        ppstat.ticks_start = ticks_start; \
    } 

#define PREPROC_PROFILE_END(ppstat) \
    if (PROFILING_PREPROCS) { \
        PROFILE_END; \
        ppstat.exits++; \
        ppstat.ticks += ticks_end - ppstat.ticks_start; \
    } 

#define PREPROC_PROFILE_REENTER_END(ppstat) \
    if (PROFILING_PREPROCS) { \
        PROFILE_END; \
        ppstat.ticks += ticks_end - ppstat.ticks_start; \
    } 

#define PREPROC_PROFILE_TMPEND(ppstat) \
    if (PROFILING_PREPROCS) { \
        PROFILE_END; \
        ppstat.ticks += ticks_end - ppstat.ticks_start; \
    } 

/************** Profiling API ******************/
void ShowRuleProfiles();

/* Preprocessor stats info */
typedef struct _PreprocStats
{
    UINT64 ticks, ticks_start;
    UINT64 checks;
    UINT64 exits;
} PreprocStats;

typedef struct _PreprocStatsNode
{
    PreprocStats *stats;
    char *name;
    int layer;
    PreprocStats *parent;
    struct _PreprocStatsNode *next;
} PreprocStatsNode;

void RegisterPreprocessorProfile(char *keyword, PreprocStats *stats, int layer, PreprocStats *parent);
void ShowPreprocProfiles();
extern PreprocStats totalPerfStats;
#else
#define PROFILE_VARS
#define OTN_PROFILE_START(otn)
#define OTN_PROFILE_END_MATCH(otn)
#define OTN_PROFILE_END_NOMATCH(otn)
#define OTN_PROFILE_NOALERT(otn)
#define OTN_PROFILE_ALERT(otn)
#define PREPROC_PROFILE_START(ppstat)
#define PREPROC_PROFILE_REENTER_START(ppstat)
#define PREPROC_PROFILE_TMPSTART(ppstat)
#define PREPROC_PROFILE_END(ppstat)
#define PREPROC_PROFILE_REENTER_END(ppstat)
#define PREPROC_PROFILE_TMPEND(ppstat)
#endif

#endif  /* __PROFILER_H__ */
