#!/usr/bin/env python
################################################################################
#
#  CDDL HEADER START
#
#  The contents of this file are subject to the terms of the Common Development
#  and Distribution License Version 1.0 (the "License").
#
#  You can obtain a copy of the license at
#  http:# www.opensource.org/licenses/CDDL-1.0.  See the License for the
#  specific language governing permissions and limitations under the License.
#
#  When distributing Covered Code, include this CDDL HEADER in each file and
#  include the License file in a prominent location with the name LICENSE.CDDL.
#  If applicable, add the following below this CDDL HEADER, with the fields
#  enclosed by brackets "[]" replaced with your own identifying information:
#
#  Portions Copyright (c) [yyyy] [name of copyright owner]. All rights reserved.
#
#  CDDL HEADER END
#
#  Copyright (c) 2017, Regents of the University of Minnesota.
#  All rights reserved.
#
#  Contributor(s):
#     Ellad B. Tadmor
#
################################################################################

# The docstring below is vc_description
'''Check that the model has no memory leaks. This is tested using the Valgrind
memory debugging tool (http://valgrind.org) by performing a series of
energy and force calculations on a randomly distorted face-centered cubic (FCC)
cube base structure for both non-periodic and periodic boundary conditions.
Separate configurations are tested for each species supported by the model, as
well as one containing a random distribution of all species. Configurations
used for testing are provided as auxiliary files.'''

# Python 2-3 compatible code issues
from __future__ import print_function
try:
   input = raw_input
except NameError:
   pass

import sys
import kimvc
import runner2
import subprocess
import re

__version__ = "001"
__author__ = "Ellad Tadmor"

################################################################################
#
#   FUNCTIONS
#
################################################################################

################################################################################
def do_vc(model, vc):
    '''
    Do memory leak check
    '''
    # call the code used to look for memory leaks durectky to generate
    # the output for the report
    runner2.memory_leaks_test_process(model, vc)

    # call memory leaks test process using valigrind to look for leaks
    try:
        valgrind_out = subprocess.check_output(['valgrind',
                                    '--suppressions=./valgrind-python.supp',
                                    'python', 'runner2.py', model],
                                    stderr=subprocess.STDOUT,
                                    universal_newlines=True)
    except:
        raise kimvc.KIMVCError('failed to run valgrind; check that it is installed.')

    # print valgrind output to report
    dashwidth = 80
    vc.rwrite('')
    vc.rwrite('='*dashwidth)
    vc.rwrite(' '*7 + 'VALGRIND OUTPUT')
    vc.rwrite('='*dashwidth)
    vc.rwrite('')
    vc.rwrite(valgrind_out)
    vc.rwrite('='*dashwidth)

    # determine grade
    vc.rwrite('')
    vc.rwrite('To pass this verification check the number of bytes that are '
              '"definitely lost" ')
    vc.rwrite('or "indirectly lost" must be zero.')
    vc.rwrite('')
    vc.rwrite('NOTE that Valgrind will typically report non-zero '
              '"possibly lost" bytes due to')
    vc.rwrite("Python's internal memory allocation and garbage collection "
              "that it does not monitor.")
    vc.rwrite('')
    definitely_lost = int(re.search('definitely lost: ([0-9]+)',
                                    valgrind_out).group(1))
    indirectly_lost = int(re.search('indirectly lost: ([0-9]+)',
                                    valgrind_out).group(1))
    leak_detected = definitely_lost > 0 or indirectly_lost > 0

    # Run again and generate full valgrind output in aux file
    try:
        valgrind_out = subprocess.check_output(['valgrind',
                                            '--leak-check=full',
                                            '--show-leak-kinds=all',
                                            'python', 'runner2.py', model],
                                            stderr=subprocess.STDOUT,
                                            universal_newlines=True)
    except:
        raise kimvc.KIMVCError('Failed to run valgrind; check that it is installed.')
    aux_file = 'valgrind.out'
    vc.vc_files.append(aux_file)
    vc.write_aux_string(aux_file, valgrind_out)
    vc.rwrite('Full Valgrind output written to auxiliary file "{}"'.
              format(aux_file))
    if leak_detected:
        vc.rwrite('(Search for the word "lost" in the file to identify '
                  'memory leaks.)')
    vc.rwrite('')

    # report grade
    if not leak_detected:
        vc_grade = 'P'
        vc_comment = 'No memory leak detected.'
    else:
        vc_grade = 'F'
        vc_comment = 'Memory leak detected.'

    return vc_grade, vc_comment

################################################################################
#
#   MAIN PROGRAM
#
###############################################################################
if __name__ == '__main__':

    vcargs = {"vc_name"        : "vc-memory-leak",
              "vc_author"      : __author__,
              "vc_description" : kimvc.vc_stripall(__doc__),
              "vc_category"    : "informational",
              "vc_grade_basis" : "passfail",
              "vc_files"       : [],
              "vc_debug"       : False}  # Set to True to get exception traceback info

    # Get the model extended KIM ID:
    model = input("Model Extended KIM ID = ")

    # Execute VC
    kimvc.setup_and_run_vc(do_vc, model, **vcargs)