#!/bin/sh

# Copyright (c) 2010-2013 Aleksey Cheusov <vle@gmx.net>
# 
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
# 
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
# 
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

set -e

usage () {
    cat <<'EOF'
Taking an output of lmdbg-stat on input lmdbg-grep outputs global
information and those stacktraces that match the specified AWK expression.

usage: lmdbg-grep -h
       lmdbg-grep [OPTIONS] <awk_expr> [files...]
OPTIONS:
  -h         display this screen
  -V         display version
  -v         invert the sense of match
EOF
}

version (){
cat <<'EOF'
lmdbg-grep 1.3.0
EOF
}

invert=0

while getopts hVv f; do
    case $f in
	h)
	    usage
	    exit 0;;
	V)
	    version
	    exit 0;;
	v)
	    invert=1;;
	'?')
	    usage
	    exit 1;;
    esac
done
shift $(expr $OPTIND - 1)

if test $# -lt 1; then
    usage
    exit 1
fi

expr="$1"
shift

if printf "%s\n" "$expr" | grep addrline > /dev/null; then
    addrline_mode=1
else
    addrline_mode=0
fi

if printf "%s\n" "$expr" | grep address > /dev/null; then
    address_mode=1
else
    address_mode=0
fi

if printf "%s\n" "$expr" | grep source > /dev/null; then
    source_mode=1
else
    source_mode=0
fi

if printf "%s\n" "$expr" | grep funcname > /dev/null; then
    funcname_mode=1
else
    funcname_mode=0
fi

/usr/bin/awk -v invert="$invert" \
      -v addrline_mode="$addrline_mode" \
      -v address_mode="$address_mode" \
      -v source_mode="$source_mode" \
      -v funcname_mode="$funcname_mode" '
BEGIN {
    ok = -1 # -1 - unknown, 0 - false, 1 - true
    count = 0
    test_addrline = (addrline_mode || address_mode || source_mode || funcname_mode)
}

function condition (){
#    print "$0=" $0
    return '"$expr"'
}

function print_accu (                   i){
    for (i=0; i < count; ++i){
	print accu [i]
    }
}

function update_skip (             cnt){
    if (test_addrline){
	if (!/^ /)
	    return
	addrline = $0
	addr = source = funcname = ""
	if (address_mode || source_mode || funcname_mode){
	    cnt = split(substr($0, 2), arr, /\t/)
	    if (cnt >= 1)
		address = arr [1]
	    if (cnt >= 2)
		source = arr [2]
	    if (cnt >= 3)
		funcname = arr [3]
	}
    }else{
	if (/^ /)
	    return
    }

    if (ok != 1)
	ok = condition()

    if (test_addrline && ok == 0)
	ok = -1
    else if (ok == 1 && !invert){
	print_accu()
	count = 0
    }
}

$0 ~ /^info / {
    print $0
    next
}

$0 ~ /^ / {
    accu [count++] = $0

    if (ok == 1){
	if (!invert)
	    print
    }else if (ok == 0){
    }else{
	update_skip()
    }

    next
}

{
    peak = max = allocs = leaks = bytes = 0
    module = op = op_type = ""
}

/^(stacktrace|malloc|calloc|realloc|(posix_)?memalign|aligned_alloc|free) / {
    if ($1 == "malloc") {
	bytes   = $3 + 0
	op      = "malloc"
	op_type = "alloc"
    }else if ($1 == "realloc"){
	bytes   = $5 + 0
	op      = "realloc"
	op_type = "alloc"
    }else if ($1 == "calloc"){
	bytes   = $3 * $5
	op      = "calloc"
	op_type = "alloc"
    }else if ($1 == "memalign" || $1 == "posix_memalign" || $1 == "aligned_alloc"){
	bytes   = $5 + 0
	op      = $1
	op_type = "alloc"
    }

    for (i=1; i <= NF; ++i){
	if ($i == "peak:"){
	    peak = $(i+1) + 0
	}else if ($i == "max:"){
	    max = $(i+1) + 0
	}else if ($i == "allocs:"){
	    allocs = $(i+1) + 0
	}else if ($i == "leaks:"){
	    leaks = $(i+1) + 0
	}else if ($i == "module:"){
	    module = $(i+1)
	}
    }
}

{
    if (ok != 1 && invert){
	print_accu()
    }

    accu [0] = $0
    count = 1

    ok = -1
    update_skip()
}

END {
    if (ok != 1 && invert){
	print_accu()
    }
}
' "$@"
