# @GPL_HEADER_START
# Use of the RAVE and related source code is subject to the terms
# of the following licenses:
# 
# GNU Public License (GPL) Rights pursuant to Version 2, June 1991
# Government Purpose License Rights (GPLR) pursuant to DFARS 252.225-7013
# 
# NO WARRANTY
# 
# ANY INFORMATION, MATERIALS, SERVICES, INTELLECTUAL PROPERTY OR OTHER
# PROPERTY OR RIGHTS GRANTED OR PROVIDED BY CARNEGIE MELLON UNIVERSITY
# PURSUANT TO THIS LICENSE (HEREINAFTER THE "DELIVERABLES") ARE ON AN
# "AS-IS" BASIS. CARNEGIE MELLON UNIVERSITY MAKES NO WARRANTIES OF ANY
# KIND, EITHER EXPRESS OR IMPLIED AS TO ANY MATTER INCLUDING, BUT NOT
# LIMITED TO, WARRANTY OF FITNESS FOR A PARTICULAR PURPOSE,
# MERCHANTABILITY, INFORMATIONAL CONTENT, NONINFRINGEMENT, OR ERROR-FREE
# OPERATION. CARNEGIE MELLON UNIVERSITY SHALL NOT BE LIABLE FOR INDIRECT,
# SPECIAL OR CONSEQUENTIAL DAMAGES, SUCH AS LOSS OF PROFITS OR INABILITY
# TO USE SAID INTELLECTUAL PROPERTY, UNDER THIS LICENSE, REGARDLESS OF
# WHETHER SUCH PARTY WAS AWARE OF THE POSSIBILITY OF SUCH DAMAGES.
# LICENSEE AGREES THAT IT WILL NOT MAKE ANY WARRANTY ON BEHALF OF
# CARNEGIE MELLON UNIVERSITY, EXPRESS OR IMPLIED, TO ANY PERSON
# CONCERNING THE APPLICATION OF OR THE RESULTS TO BE OBTAINED WITH THE
# DELIVERABLES UNDER THIS LICENSE.
# 
# Licensee hereby agrees to defend, indemnify, and hold harmless Carnegie
# Mellon University, its trustees, officers, employees, and agents from
# all claims or demands made against them (and any related losses,
# expenses, or attorney's fees) arising out of, or relating to Licensee's
# and/or its sub licensees' negligent use or willful misuse of or
# negligent conduct or willful misconduct regarding the Software,
# facilities, or other rights or assistance granted by Carnegie Mellon
# University under this License, including, but not limited to, any
# claims of product liability, personal injury, death, damage to
# property, or violation of any laws or regulations.
# 
# Carnegie Mellon University Software Engineering Institute authored
# documents are sponsored by the U.S. Department of Defense under
# Contract F19628-00-C-0003. Carnegie Mellon University retains
# copyrights in all material produced under this contract. The U.S.
# Government retains a non-exclusive, royalty-free license to publish or
# reproduce these documents, or allow others to do so, for U.S.
# Government purposes only pursuant to the copyright license under the
# contract clause at 252.227.7013.
# 
# @GPL_HEADER_END
import inspect
import logging
import errno

import os
import shutil

fslog = logging.getLogger("rave.trace.fs")
fnlog = logging.getLogger("rave.trace.functions")

def get_caller_info(lvl):
    stack = inspect.stack(0)
    # We really want the caller's caller's info (plus however many
    # levels the user specified) so...
    frame, fname, lineno, fn_name, _, _ = stack[lvl+2]
    try:
        return fname, lineno
    finally:
        del frame

def log_trace(tracelvl, msg, *args):
    try:
        if fslog.isEnabledFor(logging.DEBUG):
            msg = msg % args
            caller_fname, caller_lineno = get_caller_info(tracelvl)
            fslog.debug("%s [%s@%s]", msg, caller_fname, caller_lineno)
    except:
        # DO NOT tamper with execution
        pass

def get_error_code(e):
    try:
        return errno.errorcode[e.errno]
    except KeyError:
        return str(e.errno)





#
# Trace filesystem operations
#



def trace_open(fname, mode, tracelvl=1):
    err = "Success"
    try:
        try:
            return open(fname, mode)
        except (IOError, OSError), e:
            err = get_error_code(e)
            raise e
        except:
            err = "Failure"
            raise
    finally:
        log_trace(tracelvl, "open('%s', '%s')=%s", fname, mode, err)
        

def trace_close(f, tracelvl=1):
    try:
        fname = f.name
    except:
        fname = "[unknown]"
    err = "Success"
    try:
        try:
            return f.close()
        except (IOError, OSError), e:
            err = get_error_code(e)
            raise e
        except:
            err = "Failure"
            raise
    finally:
        log_trace(tracelvl, "close('%s')=%s", fname, err)

def trace_remove(fname, tracelvl=1):
    err = "Success"
    try:
        try:
            return os.remove(fname)
        except (IOError, OSError), e:
            err = get_error_code(e)
            raise e
        except:
            err = "Failure"
            raise
    finally:
        log_trace(tracelvl, "remove('%s')=%s", fname, err)

def trace_purge(fname, tracelvl=1):
    err = "Success"
    try:
        try:
            return os.remove(fname)
        except (IOError, OSError), e:
            err = get_error_code(e)
            raise e
        except:
            err = "Failure"
            raise
    finally:
        log_trace(tracelvl, "purge('%s')=%s", fname, err)


def trace_fdopen(fd, mode, name=None, tracelvl=1):
    if name is None:
        fname = "%d <fdopen>" % fd
    else:
        fname = "%d <%s>" % (fd, name)
    err = "Success"
    try:
        try:
            return os.fdopen(fd, mode)
        except (IOError, OSError), e:
            err = get_error_code(e)
            raise e
        except:
            err = "Failure"
            raise
    finally:
        log_trace(tracelvl, "fdopen('%s')=%s", fname, err)


def trace_move(src, dst, tracelvl=1):
    err = "Success"
    try:
        try:
            return shutil.move(src, dst)
        except (IOError, OSError), e:
            err = get_error_code(e)
            raise e
        except:
            err = "Failure"
            raise
    finally:
        log_trace(tracelvl, "move('%s', '%s')=%s", src, dst, err)



#
# Tracing Function calls
#


def trace_method_call(obj, lvl=0):
    if fnlog.isEnabledFor(logging.DEBUG):
        try:
            stack = inspect.stack(0)
            # Get caller's stack frame (or stack frame lvl frames above the
            # caller, if specified)
            frame, _, _, fn_name, _, _ = stack[lvl+1]
            try:
                fn = getattr(obj, fn_name)
                args = inspect.getargspec(fn)[0]
                argspec = []
                for a in args[1:]:
                    argspec.append("%s=%s" % (a, frame.f_locals[a]))
                fnlog.debug("%s.%s(%s)", type(obj).__name__,
                            fn_name, ", ".join(argspec))
            finally:
                del frame
        except:
            pass




def trace_function_call(lvl=0):
    if fnlog.isEnabledFor(logging.DEBUG):
        try:
            stack = inspect.stack(0)
            # Get caller's stack frame (or stack frame lvl frames above the
            # caller, if specified)
            frame, _, _, fn_name, _, _ = stack[lvl+1]
            try:
                fn = globals()[fn_name]
                args = inspect.getargspec(fn)[0]
                argspec = []
                for a in args[1:]:
                    argspec.append("%s=%s" % (a, frame.f_locals[a]))
                fnlog.debug("%s(%s)", fn_name, ", ".join(argspec))
            finally:
                del frame
        except:
            pass

    

