layers: Initial ParamChecker layer

ParamChecker layer is generated and currently only checks that ENUM input to functions or ENUMS as part of input structs have valid values.

Conflicts:
	layers/CMakeLists.txt
diff --git a/layers/CMakeLists.txt b/layers/CMakeLists.txt
index 86fc031..618e491 100644
--- a/layers/CMakeLists.txt
+++ b/layers/CMakeLists.txt
@@ -2,15 +2,15 @@
 
 macro(run_xgl_helper subcmd)
 	add_custom_command(OUTPUT ${ARGN}
-		COMMAND ${PROJECT_SOURCE_DIR}/xgl-helper.py --${subcmd} ${PROJECT_SOURCE_DIR}/include/xgl.h --abs_out_dir ${CMAKE_CURRENT_BINARY_DIR}
-		DEPENDS ${PROJECT_SOURCE_DIR}/xgl-helper.py ${PROJECT_SOURCE_DIR}/include/xgl.h
+		COMMAND ${PROJECT_SOURCE_DIR}/xgl_helper.py --${subcmd} ${PROJECT_SOURCE_DIR}/include/xgl.h --abs_out_dir ${CMAKE_CURRENT_BINARY_DIR}
+		DEPENDS ${PROJECT_SOURCE_DIR}/xgl_helper.py ${PROJECT_SOURCE_DIR}/include/xgl.h
 	)
 endmacro()
 
 macro(run_xgl_layer_generate subcmd output)
 	add_custom_command(OUTPUT ${output}
-		COMMAND ${PROJECT_SOURCE_DIR}/xgl-layer-generate.py ${subcmd} > ${output}
-		DEPENDS ${PROJECT_SOURCE_DIR}/xgl-layer-generate.py ${PROJECT_SOURCE_DIR}/xgl.py
+		COMMAND ${PROJECT_SOURCE_DIR}/xgl-layer-generate.py ${subcmd} ${PROJECT_SOURCE_DIR}/include/xgl.h > ${output}
+		DEPENDS ${PROJECT_SOURCE_DIR}/xgl-layer-generate.py ${PROJECT_SOURCE_DIR}/include/xgl.h ${PROJECT_SOURCE_DIR}/xgl.py
 	)
 endmacro()
 
@@ -67,6 +67,7 @@
 run_xgl_layer_generate(ApiDumpCpp api_dump.cpp)
 run_xgl_layer_generate(ApiDumpNoAddrCpp api_dump_no_addr.cpp)
 run_xgl_layer_generate(ObjectTracker object_track.c)
+run_xgl_layer_generate(ParamChecker param_checker.c)
 
 add_xgl_layer(Basic basic.cpp)
 add_xgl_layer(Multi multi.cpp)
@@ -80,3 +81,4 @@
 add_xgl_layer(APIDumpCpp api_dump.cpp)
 add_xgl_layer(APIDumpNoAddrCpp api_dump_no_addr.cpp)
 add_xgl_layer(ObjectTracker object_track.c)
+add_xgl_layer(ParamChecker param_checker.c)
diff --git a/xgl-layer-generate.py b/xgl-layer-generate.py
index d19a63f..7e2cb74 100755
--- a/xgl-layer-generate.py
+++ b/xgl-layer-generate.py
@@ -26,8 +26,10 @@
 #   Chia-I Wu <olv@lunarg.com>
 
 import sys
+import os
 
 import xgl
+import xgl_helper
 
 class Subcommand(object):
     def __init__(self, argv):
@@ -434,10 +436,9 @@
                             (pft, pfi) = ("%s", '"addr"')
                         log_func += '%s = %s, ' % (p.name, pft)
                         print_vals += ', %s' % (pfi)
-                        # TODO : Just want this to be simple check for params of STRUCT type
-                        if "pCreateInfo" in p.name or ('const' in p.ty and '*' in p.ty and False not in [tmp_ty not in p.ty for tmp_ty in ['XGL_CHAR', 'XGL_VOID', 'XGL_CMD_BUFFER', 'XGL_QUEUE_SEMAPHORE', 'XGL_FENCE', 'XGL_SAMPLER', 'XGL_UINT32']]):
-                            if 'Wsi' not in proto.name:
-                                cis_param_index.append(pindex)
+                        # 'format' gets special treatment as a small struct that we print inline
+                        if 'Wsi' not in proto.name and 'format' != p.name and xgl_helper.is_type(p.ty.strip('*').strip('const '), 'struct'):
+                            cis_param_index.append(pindex)
                         pindex += 1
                     log_func = log_func.strip(', ')
                     if proto.ret != "XGL_VOID":
@@ -624,13 +625,96 @@
                                  '%s%s'
                                  '%s'
                                  '}' % (qual, decl, proto.params[0].name, using_line, ret_val, c_call, create_line, destroy_line, stmt))
-
-        # TODO : Put this code somewhere so it gets called at the end if objects not deleted :
-        #     // Report any remaining objects in LL
-        #     objNode *pTrav = pObjLLHead;
-        #     while (pTrav) {
-        #         printf("WARN : %s object %p has not been destroyed.\n", pTrav->objType, pTrav->pObj);
-        #    }
+                elif "ParamChecker" == layer:
+                    # TODO : Need to fix up the non-else cases below to do param checking as well
+                    decl = proto.c_func(prefix="xgl", attr="XGLAPI")
+                    param0_name = proto.params[0].name
+                    ret_val = ''
+                    stmt = ''
+                    param_checks = []
+                    # Add code to check enums and structs
+                    # TODO : Currently only validating enum values, need to validate everything
+                    str_decl = False
+                    for p in proto.params:
+                        if xgl_helper.is_type(p.ty.strip('*').strip('const '), 'enum'):
+                            if not str_decl:
+                                param_checks.append('    char str[1024];')
+                                str_decl = True
+                            param_checks.append('    if (!validate_%s(%s)) {' % (p.ty, p.name))
+                            param_checks.append('        char str[1024];')
+                            param_checks.append('        sprintf(str, "Parameter %s to function %s has invalid value of %%i.", (int)%s);'  % (p.name, proto.name, p.name))
+                            param_checks.append('        layerCbMsg(XGL_DBG_MSG_ERROR, XGL_VALIDATION_LEVEL_0, NULL, 0, 1, "PARAMCHECK", str);')
+                            param_checks.append('    }')
+                        elif xgl_helper.is_type(p.ty.strip('*').strip('const '), 'struct') and 'const' in p.ty:
+                            if not str_decl:
+                                param_checks.append('    char str[1024];')
+                                str_decl = True
+                            if '*' in p.ty: # First check for null ptr
+                                param_checks.append('    if (!%s) {' % p.name)
+                                param_checks.append('        sprintf(str, "Struct ptr parameter %s to function %s is NULL.");'  % (p.name, proto.name))
+                                param_checks.append('        layerCbMsg(XGL_DBG_MSG_UNKNOWN, XGL_VALIDATION_LEVEL_0, NULL, 0, 1, "PARAMCHECK", str);')
+                                param_checks.append('    }')
+                                param_checks.append('    else if (!xgl_validate_%s(%s)) {' % (p.ty.strip('*').strip('const ').lower(), p.name))
+                            else:
+                                param_checks.append('    if (!xgl_validate_%s(%s)) {' % (p.ty.strip('const ').lower(), p.name))
+                            param_checks.append('        sprintf(str, "Parameter %s to function %s contains an invalid value.");'  % (p.name, proto.name))
+                            param_checks.append('        layerCbMsg(XGL_DBG_MSG_ERROR, XGL_VALIDATION_LEVEL_0, NULL, 0, 1, "PARAMCHECK", str);')
+                            param_checks.append('    }')
+                    if proto.ret != "XGL_VOID":
+                        ret_val = "XGL_RESULT result = "
+                        stmt = "    return result;\n"
+                    if proto.name == "EnumerateLayers":
+                        c_call = proto.c_call().replace("(" + proto.params[0].name, "((XGL_PHYSICAL_GPU)gpuw->nextObject", 1)
+                        funcs.append('%s%s\n'
+                                 '{\n'
+                                 '    char str[1024];\n'
+                                 '    if (gpu != NULL) {\n'
+                                 '        XGL_BASE_LAYER_OBJECT* gpuw = (XGL_BASE_LAYER_OBJECT *) %s;\n'
+                                 '        sprintf(str, "At start of layered %s\\n");\n'
+                                 '        layerCbMsg(XGL_DBG_MSG_UNKNOWN, XGL_VALIDATION_LEVEL_0, gpu, 0, 0, "PARAMCHECK", str);\n'
+                                 '        pCurObj = gpuw;\n'
+                                 '        pthread_once(&tabOnce, initLayerTable);\n'
+                                 '        %snextTable.%s;\n'
+                                 '        sprintf(str, "Completed layered %s\\n");\n'
+                                 '        layerCbMsg(XGL_DBG_MSG_UNKNOWN, XGL_VALIDATION_LEVEL_0, gpu, 0, 0, "PARAMCHECK", str);\n'
+                                 '        fflush(stdout);\n'
+                                 '    %s'
+                                 '    } else {\n'
+                                 '        if (pOutLayerCount == NULL || pOutLayers == NULL || pOutLayers[0] == NULL)\n'
+                                 '            return XGL_ERROR_INVALID_POINTER;\n'
+                                 '        // This layer compatible with all GPUs\n'
+                                 '        *pOutLayerCount = 1;\n'
+                                 '        strncpy(pOutLayers[0], "%s", maxStringSize);\n'
+                                 '        return XGL_SUCCESS;\n'
+                                 '    }\n'
+                                     '}' % (qual, decl, proto.params[0].name, proto.name, ret_val, c_call, proto.name, stmt, layer_name))
+                    elif 'DbgRegisterMsgCallback' == proto.name:
+                        funcs.append(self._gen_layer_dbg_callback_register())
+                    elif 'DbgUnregisterMsgCallback' == proto.name:
+                        funcs.append(self._gen_layer_dbg_callback_unregister())
+                    elif proto.params[0].ty != "XGL_PHYSICAL_GPU":
+                        funcs.append('%s%s\n'
+                                 '{\n'
+                                 '%s\n'
+                                 '    %snextTable.%s;\n'
+                                 '%s'
+                                 '}' % (qual, decl, "\n".join(param_checks), ret_val, proto.c_call(), stmt))
+                    else:
+                        c_call = proto.c_call().replace("(" + proto.params[0].name, "((XGL_PHYSICAL_GPU)gpuw->nextObject", 1)
+                        funcs.append('%s%s\n'
+                                 '{\n'
+                                 '    char str[1024];'
+                                 '    XGL_BASE_LAYER_OBJECT* gpuw = (XGL_BASE_LAYER_OBJECT *) %s;\n'
+                                 '    sprintf(str, "At start of layered %s\\n");\n'
+                                 '    layerCbMsg(XGL_DBG_MSG_UNKNOWN, XGL_VALIDATION_LEVEL_0, gpuw, 0, 0, "PARAMCHECK", str);\n'
+                                 '    pCurObj = gpuw;\n'
+                                 '    pthread_once(&tabOnce, initLayerTable);\n'
+                                 '    %snextTable.%s;\n'
+                                 '    sprintf(str, "Completed layered %s\\n");\n'
+                                 '    layerCbMsg(XGL_DBG_MSG_UNKNOWN, XGL_VALIDATION_LEVEL_0, gpuw, 0, 0, "PARAMCHECK", str);\n'
+                                 '    fflush(stdout);\n'
+                                 '%s'
+                                 '}' % (qual, decl, proto.params[0].name, proto.name, ret_val, c_call, proto.name, stmt))
 
         return "\n\n".join(funcs)
 
@@ -1086,6 +1170,18 @@
 
         return "\n\n".join(body)
 
+class ParamCheckerSubcommand(Subcommand):
+    def generate_header(self):
+        return '#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <assert.h>\n#include <pthread.h>\n#include "xglLayer.h"\n#include "xgl_enum_validate_helper.h"\n#include "xgl_struct_validate_helper.h"\n\nstatic XGL_LAYER_DISPATCH_TABLE nextTable;\nstatic XGL_BASE_LAYER_OBJECT *pCurObj;\nstatic pthread_once_t tabOnce = PTHREAD_ONCE_INIT;\n'
+
+    def generate_body(self):
+        body = [self._gen_layer_dbg_callback_header(),
+                self._generate_layer_dispatch_table(),
+                self._generate_dispatch_entrypoints("XGL_LAYER_EXPORT", "ParamChecker"),
+                self._generate_layer_gpa_function()]
+
+        return "\n\n".join(body)
+
 def main():
     subcommands = {
             "layer-funcs" : LayerFuncsSubcommand,
@@ -1097,14 +1193,24 @@
             "ApiDumpCpp" : ApiDumpCppSubcommand,
             "ApiDumpNoAddrCpp" : ApiDumpNoAddrCppSubcommand,
             "ObjectTracker" : ObjectTrackerSubcommand,
+            "ParamChecker" : ParamCheckerSubcommand,
     }
 
-    if len(sys.argv) < 2 or sys.argv[1] not in subcommands:
-        print("Usage: %s <subcommand> [options]" % sys.argv[0])
+    if len(sys.argv) < 3 or sys.argv[1] not in subcommands or not os.path.exists(sys.argv[2]):
+        print("Usage: %s <subcommand> <input_header> [options]" % sys.argv[0])
         print
         print("Available sucommands are: %s" % " ".join(subcommands))
         exit(1)
 
+    hfp = xgl_helper.HeaderFileParser(sys.argv[2])
+    hfp.parse()
+    xgl_helper.enum_val_dict = hfp.get_enum_val_dict()
+    xgl_helper.enum_type_dict = hfp.get_enum_type_dict()
+    xgl_helper.struct_dict = hfp.get_struct_dict()
+    xgl_helper.typedef_fwd_dict = hfp.get_typedef_fwd_dict()
+    xgl_helper.typedef_rev_dict = hfp.get_typedef_rev_dict()
+    xgl_helper.types_dict = hfp.get_types_dict()
+
     subcmd = subcommands[sys.argv[1]](sys.argv[2:])
     subcmd.run()
 
diff --git a/xgl-helper.py b/xgl_helper.py
similarity index 96%
rename from xgl-helper.py
rename to xgl_helper.py
index 67cada7..36e05ac 100755
--- a/xgl-helper.py
+++ b/xgl_helper.py
@@ -321,11 +321,13 @@
         self.string_helper_no_addr_filename = os.path.join(out_dir, self.api+"_struct_string_helper_no_addr.h")
         self.string_helper_cpp_filename = os.path.join(out_dir, self.api+"_struct_string_helper_cpp.h")
         self.string_helper_no_addr_cpp_filename = os.path.join(out_dir, self.api+"_struct_string_helper_no_addr_cpp.h")
+        self.validate_helper_filename = os.path.join(out_dir, self.api+"_struct_validate_helper.h")
         self.no_addr = False
         self.hfg = CommonFileGen(self.header_filename)
         self.cfg = CommonFileGen(self.class_filename)
         self.shg = CommonFileGen(self.string_helper_filename)
         self.shcppg = CommonFileGen(self.string_helper_cpp_filename)
+        self.vhg = CommonFileGen(self.validate_helper_filename)
         #print(self.header_filename)
         self.header_txt = ""
         self.definition_txt = ""
@@ -382,6 +384,14 @@
         self.shcppg.setBody(self._generateStringHelperFunctionsCpp())
         self.shcppg.generate()
 
+    # Generate c-style .h file that contains functions for printing structs
+    def generateValidateHelper(self):
+        print("Generating struct validate helper")
+        self.vhg.setCopyright(self._generateCopyright())
+        self.vhg.setHeader(self._generateValidateHelperHeader())
+        self.vhg.setBody(self._generateValidateHelperFunctions())
+        self.vhg.generate()
+
     def _generateCopyright(self):
         return "//This is the copyright\n"
 
@@ -445,8 +455,14 @@
         dp_funcs.append("}\n")
         return "\n".join(dp_funcs)
 
+    def _get_func_name(self, struct, mid_str):
+        return "%s_%s_%s" % (self.api, mid_str, struct.lower().strip("_"))
+
     def _get_sh_func_name(self, struct):
-        return "%s_print_%s" % (self.api, struct.lower().strip("_"))
+        return self._get_func_name(struct, 'print')
+
+    def _get_vh_func_name(self, struct):
+        return self._get_func_name(struct, 'validate')
 
     # Return elements to create formatted string for given struct member
     def _get_struct_print_formatted(self, struct_member, pre_var_name="prefix", postfix = "\\n", struct_var_name="pStruct", struct_ptr=True, print_array=False):
@@ -863,6 +879,31 @@
         header.append("string dynamic_display(const XGL_VOID* pStruct, const string prefix);\n")
         return "".join(header)
 
+    def _generateValidateHelperFunctions(self):
+        sh_funcs = []
+        # We do two passes, first pass just generates prototypes for all the functsions
+        for s in self.struct_dict:
+            sh_funcs.append('uint32_t %s(const %s* pStruct);' % (self._get_vh_func_name(s), typedef_fwd_dict[s]))
+        sh_funcs.append('\n')
+        for s in self.struct_dict:
+            sh_funcs.append('uint32_t %s(const %s* pStruct)\n{' % (self._get_vh_func_name(s), typedef_fwd_dict[s]))
+            for m in sorted(self.struct_dict[s]):
+                if is_type(self.struct_dict[s][m]['type'], 'enum'):
+                    sh_funcs.append('    if (!validate_%s(pStruct->%s))\n        return 0;' % (self.struct_dict[s][m]['type'], self.struct_dict[s][m]['name']))
+            sh_funcs.append("    return 1;\n}")
+
+        return "\n".join(sh_funcs)
+
+    def _generateValidateHelperHeader(self):
+        header = []
+        header.append("//#includes, #defines, globals and such...\n")
+        for f in self.include_headers:
+            if 'xgl_enum_validate_helper' not in f:
+                header.append("#include <%s>\n" % f)
+        header.append('#include "xgl_enum_validate_helper.h"\n\n// Function Prototypes\n')
+        #header.append("char* dynamic_display(const XGL_VOID* pStruct, const char* prefix);\n")
+        return "".join(header)
+
     def _generateHeader(self):
         header = []
         header.append("//#includes, #defines, globals and such...\n")
@@ -940,7 +981,7 @@
 
     def generateEnumValidate(self):
         self.evhfg.setHeader(self._generateSHHeader())
-        self.evhfg.setHeader(self._generateVHBody())
+        self.evhfg.setBody(self._generateVHBody())
         self.evhfg.generate()
 
     def _generateVHBody(self):
@@ -1301,6 +1342,7 @@
         print("Generating struct wrapper class to %s" % sw.class_filename)
         sw.generateBody()
         sw.generateStringHelper()
+        sw.generateValidateHelper()
         # Generate a 2nd helper file that excludes addrs
         sw.set_no_addr(True)
         sw.generateStringHelper()