Please enable JavaScript to view this site.

Thread Validator Help

 

The Thread Validator stub service libraries

 

The NT Service API is very simple, consisting of functions to load, start and unload the Thread Validator DLL.

 

We have also provided some debugging functions to help you debug the implementation of the NT Service API because getting data into and out of services is not always straightforward.

 

The stub service libraries used for this are shown in the following table:

 


32 bit Thread Validator

64 bit Thread Validator

32 bit service

svlTVStubService.lib

svlTVStubServiceMD.lib

svlTVStubServiceMT.lib

svlTVStubService.lib

svlTVStubServiceMD.lib

svlTVStubServiceMT.lib

64 bit service

N/A

svlTVStubService_x64.lib

svlTVStubServiceMD_x64.lib

svlTVStubServiceMT_x64.lib

 

All the functions exported from these libraries are exported as extern "C" so that C and C++ users can use them.

 

Library name suffixes

 

The MD suffix indicates the library was built with the /MD compiler switch.

The MT suffix indicates the library was built with the /MT compiler switch.

 

2010 or 2012?

 

If you are using Visual Studio 2010 or earlier, use libraries from a directory with 2010 in the directory name.

 

If you are using Visual Studio 2012 or later, use libraries from a directory with 2012 in the directory name.

 

 

Header files

 

The simple header file svlTVStubService.h and svlServiceError.h is found in the svlTVStubService directory of the install area.

 

The header files provides an error enumeration and the API functions.

 

 

API changes - February 2018

 

To make the API easier to use with services we changed the API by adding many debugging functions to allow you to easily log information. We also extended the error enumeration to provide additional error status values.

 

We also split the function of loading and starting the validator into two functions - a load function and a start function.

 

We split the functionality so that you could setup a service callback prior to calling the start function. The service callback allows the service control manager to be informed that the service is still active during time consuming operations, such as starting the validator when the service is non-trivial in scope. Failure to inform the service control manager results in the service being killed by the service control manager because it thinks the service has hung. This change in the API is to ensure you get better results from using our software.

 

What do you need to do to move from the old API to the new API?

 

Change all SVL_SERVICE_ERROR declarations to SVL_SERVICE_ERROR.

 

Your previous startup code probably looked like this:

 

   SVL_SERVICE_ERROR errCode;
 
   errCode = svlTVStub_LoadThreadValidator();

 

Change it to this:

 

   SVL_SERVICE_ERROR errCode;
 
   errCode = svlTVStub_LoadThreadValidator();
 
   errCode = svlTVStub_SetServiceCallback(serviceCallback, NULL);
 
   errCode = svlTVStub_StartThreadValidator();

 

The serviceCallback would look something like this:

 

   void serviceCallback(void   *userParam)
   {
       static DWORD dwCheckPoint = 1;
   
       ssStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
       ssStatus.dwServiceSpecificExitCode = 0;
 
       ssStatus.dwControlsAccepted = 0;
      
       ssStatus.dwCurrentState = dwCurrentState;
       ssStatus.dwWin32ExitCode = dwWin32ExitCode;
       ssStatus.dwWaitHint = dwWaitHint;
       ssStatus.dwCheckPoint = dwCheckPoint++;
      
       // Report the status of the service to the service control manager.
 
       return SetServiceStatus(sshStatusHandle, &ssStatus);
   }

 

In the code above we have omitted error handling. To see how to use the new logging function with error handling please examine the source code service.cpp in the example service project.

 

Important.

 

Once your service is running (rather than starting) your service callback should set the appropriate running status SERVICE_RUNNING rather than SERVICE_START_PENDING.

An alternative solution is to prevent the service callback from being called once the service status has been set to running. Do this by calling svlCVStub_SetServiceCallback(NULL, NULL);.

 

API Reference

 

SVL_SERVICE_ERROR Enumeration

 

typedef enum _svlServiceError
{
   SVL_OK,                                                              // Normal behaviour
   SVL_ALREADY_LOADED,                                                  // Stub DLL already loaded into service
   SVL_LOAD_FAILED,                                                     // Failed to load stub DLL into service
   SVL_FAILED_TO_ENABLE_STUB_SYMBOLS,                                   // Loaded DLL, but failed to enable stub symbols because couldn't find function
   SVL_NOT_LOADED,                                                      // Couldn't unload DLL because DLL not loaded
   SVL_FAIL_UNLOAD,                                                     // Couldn't unload DLL because couldn't find function
   SVL_FAIL_TO_CLEANUP_INTERNAL_HEAP,                                   // Couldn't get the internal stub heap and thus couldn't clean it up
   SVL_FAIL_MODULE_HANDLE                                               // Couldn't get the stub DLL handle so couldn't continue
   SVL_FAIL_SETSERVICECALLBACK,                                         // Couldn't call the set service callback

   SVL_FAIL_COULD_NOT_FIND_ENTRY_POINT,                                 // Couldn't find the DLL entry point to start the validator

   SVL_FAIL_TO_START,                                                   // Failed to start the Validator

   SVL_FAIL_SETSERVICECALLBACKTHRESHOLD,                                // Couldn't call the set service callback threshold

   SVL_FAIL_PATHS_DO_NOT_MATCH,                                        // Path to service in env vars doesn't match the service being run

   SVL_FAIL_INCORRECT_PRODUCT_PREFIX,                                // Wrong validator

   SVL_FAIL_X86_VALIDATOR_FOUND_EXPECTED_X64_VALIDATOR,        // Found wrong bit depth validator

   SVL_FAIL_X64_VALIDATOR_FOUND_EXPECTED_X86_VALIDATOR,        // Found wrong bit depth validator

   SVL_FAIL_DID_YOU_MONITOR_A_SERVICE_FROM_VALIDATOR,                // Looks like Monitor A Service wasn't used

   SVL_FAIL_ENV_VAR_NOT_FOUND,                                        // Env Var not found

   SVL_FAIL_VALIDATOR_ENV_VAR_NOT_FOUND,                                // Env Var identifying validator not found

   SVL_FAIL_VALIDATOR_ID_NOT_SPECIFIED,                                // Validator process not specified

   SVL_FAIL_VALIDATOR_ID_NOT_A_PROCESS,                                // Validator process identified doesn't exist

   SVL_FAIL_VALIDATOR_NOT_FOUND,                                        // Validator process identified doesn't exist

} SVL_SERVICE_ERROR;

 

The API consists of the following functions. Each function will be documented in detail after the list of functions.

 

extern "C" SVL_SERVICE_ERROR svlTVStub_LoadThreadValidator();
 
extern "C" SVL_SERVICE_ERROR svlTVStub_LoadThreadValidator6432();
 
extern "C" SVL_SERVICE_ERROR svlTVStub_StartThreadValidator();

 

extern "C" SVL_SERVICE_ERROR svlTVStub_StartThreadValidatorForIIS();
 
extern "C" SVL_SERVICE_ERROR svlTVStub_UnloadThreadValidator();
 
extern "C" SVL_SERVICE_ERROR svlTVStub_SetServiceCallback(serviceCallback_FUNC callback,
                                                          void*                userParam);
 
// debugging functions
 
extern "C" void svlTVStub_setLogFileName(const wchar_t* fileName);
 
extern "C" void svlTVStub_deleteLogFile();
 
extern "C" void svlTVStub_writeToLogFileA(const char* text);
 
extern "C" void svlTVStub_writeToLogFileW(const wchar_t* text);
 
extern "C" void svlTVStub_writeToLogFile(SVL_SERVICE_ERROR errCode);
 
extern "C" void svlTVStub_writeToLogFileLastError(DWORD errCode);
 
extern "C" void svlTVStub_dumpPathToLogFile();

 

 

Which function you should call is shown in the table below.

 


32 bit Thread Validator

64 bit Thread Validator

32 bit service

svlTVStub_LoadThreadValidator()

svlTVStub_LoadThreadValidator6432()

64 bit service

N/A

svlTVStub_LoadThreadValidator()

 

 

Loading the Thread Validator DLL into your service

 

To load the Thread Validator stub dll svlThreadValidatorStub(_x64).dll into your service, use the function below, not LoadLibrary().

 

extern "C" SVL_SERVICE_ERROR svlTVStub_LoadThreadValidator();

 

This loads the DLL and sets up a few internal variables in the DLL to ensure that symbols are sent from the stub to the Thread Validator user interface.

 

This is necessary because the Thread Validator user interface can't open a process handle to a service and so is unable to get symbols from the process.

 

To solve this, symbols are sent from the stub to the user interface as needed.

 

If you just call LoadLibrary() on the DLL, symbols will not be sent to the Thread Validator user interface and you won't get meaningful function names in your stack trace information.

 

This function can be used when monitoring:

 

32 bit services or applications with Thread Validator
 

64 bit services or applications with Thread Validator x64

 

warningnote There is another version of the function that you should use when monitoring 32 bit builds with Thread Validator x64

 

extern "C" SVL_SERVICE_ERROR svlTVStub_LoadThreadValidator6432();

 

 

Shutting down the Thread Validator DLL from your service.

 

Similarly to starting, you'll need to use the following to shutdown the Thread Validator stub dll.

 

extern "C" SVL_SERVICE_ERROR svlTVStub_ShutdownThreadValidator();

 

This sends the shutting down notification and removes any hooks for your process.

 

 

Unloading the Thread Validator DLL from your service.

 

Use this method to unload the Thread Validator stub dll, and not FreeLibrary().

 

extern "C" SVL_SERVICE_ERROR svlTVStub_UnloadThreadValidator();

 

 

Setting a service notification callback

 

Once you have successfully loaded the Thread Validator DLL you can setup a service callback so that the service control manager can be kept updated during the process of starting the validator.

 

When a service is starting, Windows requires the service to inform the Service Control Manager (SCM) that is starting at least every ten seconds.

 

Failure to do so results in Windows concluding that the service has failed to start, and the service is terminated.

 

Instrumenting your service may well take more than 10 seconds, depending on the complexity and size of your service.

 

The solution is for Thread Validator to periodically call a user supplied callback from which you can regularly inform the SCM of the appropriate status.

 

extern "C" SVL_SERVICE_ERROR svlTVStub_SetServiceCallback(serviceCallback_FUNC callback, void *userParam);

 

userParam is a value you can supply which will then be passed to the callback every time the callback is called during instrumentation.

 

Here is an example callback which ignores the userParam.

 

   void serviceCallback(void   *userParam)
   {
       static DWORD dwCheckPoint = 1;
   
       ssStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
       ssStatus.dwServiceSpecificExitCode = 0;
 
       ssStatus.dwControlsAccepted = 0;
      
       ssStatus.dwCurrentState = dwCurrentState;
       ssStatus.dwWin32ExitCode = dwWin32ExitCode;
       ssStatus.dwWaitHint = dwWaitHint;
      
       ssStatus.dwCheckPoint = dwCheckPoint++;
      
       // Report the status of the service to the service control manager.
 
       return SetServiceStatus(sshStatusHandle, &ssStatus);
   }

 

 

Starting Thread Validator DLL in your service

 

Once you have successfully loaded the Thread Validator DLL you can start the Validator inspecting the service.

 

extern "C" SVL_SERVICE_ERROR svlTVStub_StartThreadValidator();

 

 

Setting a filename for all logging data to be written to

 

To use any of the following logging functions you first need to set the name of the filename used for logging.

Setting this filename also sets the filename used by some of these API functions - you will find additional logging data from those functions that will help debug any issues with the service.

 

extern "C" void svlTVStub_setLogFileName(const wchar_t* fileName);
 

 

Deleting the logfile

 

This function deletes the log file.

 
extern "C" void svlTVStub_deleteLogFile();
 

 

Writing ANSI text to the logfile

 

Call this function to write a standard ANSI character string to the log file.

 
extern "C" void svlTVStub_writeToLogFileA(const char* text);
 

 

Write UNICODE text to the logfile

 

Call this function to write a unicode character string to the log file. The characters are cast to ANSI prior to writing. As such you can't write Korean to the log file. This is simply a convenience function to write wide characters as simply as ANSI characters.

 
extern "C" void svlTVStub_writeToLogFileW(const wchar_t* text);
 

 

Writing error code descriptions to the logfile

 

Call this function to write a human readable description of the SVL_SERVICE_ERROR error code to the log file.

 
extern "C" void svlTVStub_writeToLogFile(SVL_SERVICE_ERROR errCode);
 

 

Writing LastError code descriptions to the logfile

 

Call this function to write a human readable description of the Windows error code to the log file.

 
extern "C" void svlTVStub_writeToLogFileLastError(DWORD errCode);
 

 

Dumping the PATH environment to the logfile

 

Call this function to write the contents of the PATH environment variable to the log file. This can be useful if you want to know what the search path is when trying to debug why a DLL wasn't found during an attempt to load the Validator DLL.

 
extern "C" void svlTVStub_dumpPathToLogFile();

 

 

Thread Validator DLL behaviour

 

The DLL prepares itself in different ways and shuts itself down differently depending on whether:

 

The DLL is directly linked to the application for use with the API or injected with Thread Validator

 

The DLL expects to oversee and manage the application shutdown.

 

The DLL is loaded by using the function above

 

The DLL expects to be removed prior to application shutdown and the its behaviour is undefined once you enter the program shutdown sequence.

 

This difference in behaviour is intentional and is done to allow the use of the stub DLL in services.

 

 

Linker Problems

 

Some linkers cannot link the stub service library file.  If you have this problem see What do I do if I cannot use svlTVStubService.lib?

 

 

Troubleshooting - Service fails to start

 

If a service takes too long to start the service control manager kills the service.

 

The way to stop this is for a service to call ReportStatusToSCMgr() to tell the service control manager that the service is still OK.

 

Thread Validator can't do this for you as the call requires some data from any earlier call you have made.

 

The solution is that you provide a callback using svlTVStub_SetServiceCallback() that Thread Validator can call during the process of attaching to the service, and you can call the appropriate function.

 

Example code to set the callback:

 

 errCode = svlTVStub_SetServiceCallback(serviceCallback,                // the callback

                                              NULL);                                // some user data (we don't have any, so set NULL)

 if (bLogging)

 {

         if (errCode != SVL_OK)

         {

                 svlTVStub_writeToLogFileW(L"Setting service callback failed. \r\n");

                 svlTVStub_writeToLogFile(errCode);

         }

 

         svlTVStub_writeToLogFileW(L"Starting Thread Validator\r\n");

 }

 

 

 

Example callback:

 

static void serviceCallback(void        *userParam)

{

 // just tell the Service Control Manager that we are still busy

 // in this example userParam is not used

 //

 // note that prior to the Validator loading it's DLL ssStatus.dwCurrentState must have been initialised, most likely to SERVICE_START_PENDING

 // you could pass a fixed value here, but it would need to change once the service has finished starting up so that you don't unintentionally change the service state

 // when this callback is called. This callback is called whenever instrumentation happens (when a DLL is loaded). Thus you can't assume this is only called during service startup,

 // it may also get called later in the service lifetime.

 

 ReportStatusToSCMgr(ssStatus.dwCurrentState,        // service state

                         NO_ERROR,                        // exit code

                         3000);                                // wait hint

}

 

We strongly recommend that you set a service callback. It won't harm your program and it will remove any likelihood of your service being killed by the service control manager.

 

 

Troubleshooting - Service starts, Thread Validator gets no data

 

If you have problems getting Thread Validator to monitor your service you'll need to find out what's failing.

 

Until Thread Validator loads correctly and successfully connects to the graphical user interface you have no way of knowing what is happening.

 

The solution is to set a log file that Thread Validator can write status messages to. You can also write your own status messages to this log file.

 

Set the log file using svlTVStub_setLogFileName. Write to it using svlTVStub_writeToLogFile(), svlTVStub_writeToLogFileA(), svlTVStub_writeToLogFileW().

 

Then when things are not working as expected take a look at the log file to see the errors. The Thread Validator will often suggest what the problem is.

 

We strongly recommend that you configure the log file and use it when working with services. It has saved us a lot of time.

 

 

Examples

 

Example demonstrating how to monitor a service.

 

Example demonstrating how to monitor an application launched from a service (how to monitor any application running on a service account).