/* cilkview.h                  -*-C++-*-
 *
 *************************************************************************
 *
 * @copyright
 * Copyright (C) 2010-2013, Intel Corporation
 * All rights reserved.
 * 
 * @copyright
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 
 *   * Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *   * 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.
 *   * Neither the name of Intel Corporation nor the names of its
 *     contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 * 
 * @copyright
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT
 * HOLDER 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.
 *
 **************************************************************************/

#ifndef INCLUDED_CILKVIEW_H
#define INCLUDED_CILKVIEW_H

#include <cilk/cilk_api.h>

#ifdef _WIN32
#   ifndef _WINBASE_
__CILKRTS_BEGIN_EXTERN_C
unsigned long __stdcall GetTickCount();
__CILKRTS_END_EXTERN_C
#   endif
#endif  // _WIN32

#if defined __unix__ || defined __APPLE__ || defined __VXWORKS__
#   include <sys/time.h>
#endif  // defined __unix__ || defined __APPLE__

/// @brief Return the system clock with millisecond resolution
///
/// This function returns a long integer representing the number of
/// milliseconds since an arbitrary starting point, e.g., since the system was
/// started or since the Unix Epoch.  The result is meaningless by itself, but
/// the difference between two sequential calls to __cilkview_getticks()
/// represents the time interval that elapsed between them (in ms).
static inline unsigned long long __cilkview_getticks()
{
#if __INTEL_COMPILER > 1200
    // When inlined, prevent code motion around this call
    __notify_zc_intrinsic((void*) "test_getticks_start", 0);
#endif

#ifdef _WIN32
    // Return milliseconds elapsed since the system started
    return GetTickCount();
#elif defined(__unix__) || defined(__APPLE__) || defined __VXWORKS__
    // Return milliseconds elapsed since the Unix Epoch
    // (1-Jan-1970 00:00:00.000 UTC)
    struct timeval t;
    gettimeofday(&t, 0);
    return t.tv_sec * 1000ULL + t.tv_usec / 1000;
#else
#   error test_getticks() not implemented for this OS
#endif

#if __INTEL_COMPILER > 1200
    // When inlined, prevent code motion around this call
    __notify_zc_intrinsic((void*) "test_getticks_end", 0);
#endif
}

typedef struct
{
    unsigned int        size;           // Size of structure in bytes
    unsigned int        status;         // 1 = success, 0 = failure
    unsigned long long  time;           // Time in milliseconds
    unsigned long long  work;
    unsigned long long  span;
    unsigned long long  burdened_span;
    unsigned long long  spawns;
    unsigned long long  syncs;
    unsigned long long  strands;
    unsigned long long  atomic_ins;
    unsigned long long  frames;
} cilkview_data_t;

typedef struct
{
    cilkview_data_t *start;     // Values at start of interval
    cilkview_data_t *end;       // Values at end of interval
    const char *label;          // Name for this interval
    unsigned int flags;         // What to do - see flags below
} cilkview_report_t;

// What __cilkview_report should do.  The flags can be ORed together
enum
{
    CV_REPORT_WRITE_TO_LOG = 1,     // Write parallelism report to the log (xml or text)
    CV_REPORT_WRITE_TO_RESULTS = 2  // Write parallelism data to results file
};

#ifndef CILKVIEW_NO_REPORT
static void __cilkview_do_report(cilkview_data_t *start,
                          cilkview_data_t *end,
                          const char *label,
                          unsigned int flags);
#endif /* CILKVIEW_NO_REPORT */

/*
 * Metacall data
 *
 * A metacall is a way to pass data to a function implemented by a tool.
 * Metacalls are always instrumented when the tool is loaded.
 */

// Tool code for Cilkview
#define METACALL_TOOL_CILKVIEW 2

// Metacall codes implemented by Cilkview
enum
{
    CV_METACALL_PUTS,
    CV_METACALL_QUERY,
    CV_METACALL_START,
    CV_METACALL_STOP,
    CV_METACALL_RESET,
    CV_METACALL_USE_DEFAULT_GRAIN,
    CV_METACALL_CONNECTED,
    CV_METACALL_SUSPEND,
    CV_METACALL_RESUME,
    CV_METACALL_REPORT
};

#if ! defined(CILK_STUB) && defined(__INTEL_COMPILER)
#  define __cilkview_metacall(code,data) \
    __cilkrts_metacall(METACALL_TOOL_CILKVIEW, code, data)
#else
#  define __cilkview_metacall(annotation,expr) (annotation, (void) (expr))
#endif

// Write arbitrary string to the log
#define __cilkview_puts(arg) \
    __cilkview_metacall(CV_METACALL_PUTS, arg)

// Retrieve the Cilkview performance counters.  The parameter must be a 
// cilkview_data_t
#define __cilkview_query(d)                             \
    do {                                                \
        d.size = sizeof(d);                             \
        d.status = 0;                                   \
        __cilkview_metacall(CV_METACALL_QUERY, &d);     \
        if (0 == d.status)                              \
            d.time = __cilkview_getticks();             \
    } while (0)

// Write report to log or results file. If end is NULL, Cilkview will
// use the current values.
#define __cilkview_report(start, end, label, flags) \
    __cilkview_do_report(start, end, label, flags)

// Control the workspan performance counters for the final report
#define __cilkview_workspan_start() \
    __cilkview_metacall(CV_METACALL_START, 0)
#define __cilkview_workspan_stop() \
    __cilkview_metacall(CV_METACALL_STOP, 0)
#define __cilkview_workspan_reset() \
    __cilkview_metacall(CV_METACALL_RESET, 0)
#define __cilkview_workspan_suspend() \
    __cilkview_metacall(CV_METACALL_SUSPEND, 0)
#define __cilkview_workspan_resume() \
    __cilkview_metacall(CV_METACALL_RESUME, 0)

#define __cilkview_use_default_grain_size() \
    __cilkview_metacall(CV_METACALL_USE_DEFAULT, 0)

// Sets the int is_connected to 1 if Cilkview is active
#define __cilkview_connected(is_connected) \
    __cilkview_metacall(CV_METACALL_CONNECTED, &is_connected)


#ifndef CILKVIEW_NO_REPORT

// Stop Microsoft include files from complaining about getenv and fopen
#define _CRT_SECURE_NO_WARNINGS

#include <stdlib.h>
#include <stdio.h>

#ifdef _WIN32
#pragma warning(push)
#pragma warning(disable: 1786)	// Suppress warnings that getenv, fopen are deprecated
#endif

static void __cilkview_do_report(cilkview_data_t *start,
                                 cilkview_data_t *end,
                                 const char *label,
                                 unsigned int flags)
{
    int under_cilkview = 0;
    unsigned long long elapsed_ms;
    int worker_count = 0;
    char *nworkers;
    char *outfile;
    FILE *f;

    // Check whether we're running under Cilkview
    __cilkview_connected(under_cilkview);

    // If we're running under Cilkview, let it do those things that need
    // to be done
    if (under_cilkview)
    {
        cilkview_report_t d = {start, end, label, flags};
        __cilkview_metacall(CV_METACALL_REPORT, &d);
        return;
    }

    // We're not running under Cilkview.
    //
    // If we weren't asked to write to the results file, we're done.
    if (0 == (flags & CV_REPORT_WRITE_TO_RESULTS))
        return;

    // Calculate the elapse milliseconds
    if (NULL == end)
        elapsed_ms = __cilkview_getticks() - start->time;
    else
        elapsed_ms = end->time - start->time;

    // Determine how many workers we're using for this trial run
    nworkers = getenv("CILK_NWORKERS");
    if (NULL != nworkers)
        worker_count = atoi(nworkers);
    if (0 == worker_count)
        worker_count = 16;

    // Open the output file and write the trial data to it
    outfile = getenv("CILKVIEW_OUTFILE");
    if (NULL == outfile)
        outfile = (char *)"cilkview.out";

    f = fopen(outfile, "a");
    if (NULL == f)
        fprintf(stderr, "__cilkview_do_report: unable to append to file %s\n", outfile);
    else
    {
        fprintf(f, "%s trial %d %f\n", label,
                worker_count,
                ((float)elapsed_ms) / 1000.0f);
        fclose(f);
    }
}
#ifdef _WIN32
#pragma warning(pop)
#endif

#endif  // CILKVIEW_NO_REPORT


#endif /* ! defined(INCLUDED_CILKVIEW_H) */