view common/portability/src/main/native/WindowsHelperImpl.c @ 2575:902e2e96f4e8

A preliminary port of Thermostat to macos. It is quite usable except for statistics that query the OS (like memory and IO usage over time). The main unfinished business: - native code for memory and IO usage - JUnit - GUI often hangs on exit. - Mac .app bundle build Reviewed-by: neugens Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2017-January/022086.html
author Simon Tooke <stooke@redhat.com>
date Mon, 30 Jan 2017 13:06:07 -0500
parents b98beac5559c
children 648dc28c87ed
line wrap: on
line source

/*
 * Copyright 2012-2017 Red Hat, Inc.
 *
 * This file is part of Thermostat.
 *
 * Thermostat is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published
 * by the Free Software Foundation; either version 2, or (at your
 * option) any later version.
 *
 * Thermostat is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Thermostat; see the file COPYING.  If not see
 * <http://www.gnu.org/licenses/>.
 *
 * Linking this code with other modules is making a combined work
 * based on this code.  Thus, the terms and conditions of the GNU
 * General Public License cover the whole combination.
 *
 * As a special exception, the copyright holders of this code give
 * you permission to link this code with independent modules to
 * produce an executable, regardless of the license terms of these
 * independent modules, and to copy and distribute the resulting
 * executable under terms of your choice, provided that you also
 * meet, for each linked independent module, the terms and conditions
 * of the license of that module.  An independent module is a module
 * which is not derived from or based on this code.  If you modify
 * this code, you may extend this exception to your version of the
 * library, but you are not obligated to do so.  If you do not wish
 * to do so, delete this exception statement from your version.
 */

#include "com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl.h"
#include <jni.h>
#include <unistd.h>
#include <string.h>

#if !defined(_WIN32)
# include <netdb.h>
#else // windows
# include <winsock2.h>
# include <psapi.h>
# include <intrin.h>
#endif

#define MAX_NAME 256
#ifndef NI_MAXHOST
#define NI_MAXHOST 1025
#endif /* NI_MAXHOST */

#if defined(DEBUG)
static void testLength(JNIEnv* env, jlongArray array, int minLength) {
    // sanity test
    jsize len = (*env)->GetArrayLength(env, array);
    assert(len >= minLength);
}
#else
static void testLength(JNIEnv* env, jlongArray array, int minLength) {}
#endif

/*
 * Class:     com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl
 * Method:    getHostName0
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL
Java_com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl_getHostName0
  (JNIEnv *env, jclass winHelperClass, jboolean prependDomain)
{
      char hostname[NI_MAXHOST];
      memset(hostname, 0, sizeof(hostname));

      if (gethostname(hostname,  sizeof(hostname)) == 0) {
          return (*env)->NewStringUTF(env, hostname);
      }
      return NULL;
}

/*
 * Class:     com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl
 * Method:    getOSVersion0
 * Signature: ([J)V
 */
JNIEXPORT void JNICALL Java_com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl_getOSVersion0
  (JNIEnv *env, jclass winHelperClass, jlongArray array)
{
    testLength(env, array, 3);

    // Get the element pointer
    jlong* data = (*env)->GetLongArrayElements(env, array, 0);

    OSVERSIONINFOEX vinfo;
    vinfo.dwOSVersionInfoSize = sizeof(vinfo);
    GetVersionEx(&vinfo);
    data[0] = vinfo.dwMajorVersion;
    data[1] = vinfo.dwMinorVersion;
    data[2] = vinfo.dwBuildNumber;

    (*env)->ReleaseLongArrayElements(env, array, data, 0);
}


/*
 * Class:     com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl
 * Method:    getGlobalMemoryStatus0
 * Signature: ([J)V
 */
JNIEXPORT boolean JNICALL Java_com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl_getGlobalMemoryStatus0
  (JNIEnv *env, jclass winHelperClass, jlongArray array)
{
    testLength(env, array, 8);

    // Get the element pointer
    jlong* data = (*env)->GetLongArrayElements(env, array, 0);

    // get the memory info
    MEMORYSTATUSEX statex;
    statex.dwLength = sizeof(statex);
    GlobalMemoryStatusEx(&statex);
    data[0] = statex.dwMemoryLoad;
    data[1] = statex.ullTotalPhys;
    data[2] = statex.ullAvailPhys;
    data[3] = statex.ullTotalPageFile;
    data[4] = statex.ullAvailPageFile;
    data[5] = statex.ullTotalVirtual;
    data[6] = statex.ullAvailVirtual;
    data[7] = statex.ullAvailExtendedVirtual;

    (*env)->ReleaseLongArrayElements(env, array, data, 0);
    return TRUE;
}


/*
 * Class:     com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl
 * Method:    getCPUString0
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl_getCPUString0
  (JNIEnv *env, jclass winHelperClass)
{
    // Get extended ids.
    int CPUInfo[4] = {-1};
    __cpuid(CPUInfo, 0x80000000);
    unsigned int nExIds = CPUInfo[0];

    // Get the information associated with each extended ID.
    char CPUBrandString[0x40] = { 0 };
    for( unsigned int i=0x80000000; i<=nExIds; ++i)
    {
        __cpuid(CPUInfo, i);

        // Interpret CPU brand string and cache information.
        if  (i == 0x80000002)
        {
            memcpy( CPUBrandString,
            CPUInfo,
            sizeof(CPUInfo));
        }
        else if( i == 0x80000003 )
        {
            memcpy( CPUBrandString + 16,
            CPUInfo,
            sizeof(CPUInfo));
        }
        else if( i == 0x80000004 )
        {
            memcpy(CPUBrandString + 32, CPUInfo, sizeof(CPUInfo));
        }
    }
    return (*env)->NewStringUTF(env, CPUBrandString);

}

/*
 * Class:     com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl
 * Method:    getCPUCount0
 * Signature: ()I
 */
JNIEXPORT jint JNICALL Java_com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl_getCPUCount0
  (JNIEnv *env, jclass winHelperClass)
{
    SYSTEM_INFO sysinfo;
    GetSystemInfo(&sysinfo);

    return sysinfo.dwNumberOfProcessors;
}

/*
 * Class:     com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl
 * Method:    queryPerformanceFrequency0
 * Signature: ()I
 */
JNIEXPORT jlong JNICALL Java_com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl_queryPerformanceFrequency0
  (JNIEnv *env, jclass winHelperClass)
{
    LARGE_INTEGER freq;
    QueryPerformanceFrequency(&freq);

    return (jlong)(freq.QuadPart);
}

/*
 * Class:     com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl
 * Method:    getProcessSID0
 * Signature: (I)I
 */
JNIEXPORT jstring JNICALL
Java_com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl_getProcessSID0
  (JNIEnv *env, jclass winHelperClass, jint pid)
{
    HANDLE hProcess;

    hProcess = (pid == 0) ? GetCurrentProcess() : OpenProcess( PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid );
    if (NULL == hProcess)
        return NULL;

    HANDLE hToken = NULL;

    if( !OpenProcessToken( hProcess, TOKEN_QUERY, &hToken ) ) {
        CloseHandle( hProcess );
        return NULL;
    }

    DWORD dwSize = MAX_NAME;
    DWORD dwLength = 0;
    PTOKEN_USER ptu = NULL;

    if (!GetTokenInformation(
        hToken,         // handle to the access token
        TokenUser,      // get information about the token's groups
        (LPVOID) ptu,   // pointer to PTOKEN_USER buffer
        0,              // size of buffer
        &dwLength       // receives required buffer size
    )) {
        if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
            CloseHandle( hToken );
            CloseHandle( hProcess );
            return NULL;
        }

        ptu = (PTOKEN_USER)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwLength);

        if (ptu == NULL) {
            CloseHandle( hToken );
            CloseHandle( hProcess );
            return NULL;
        }
    }

    if (!GetTokenInformation(
         hToken,         // handle to the access token
         TokenUser,      // get information about the token's groups
         (LPVOID) ptu,   // pointer to PTOKEN_USER buffer
         dwLength,       // size of buffer
         &dwLength       // receives required buffer size
         )) {
        if (ptu != NULL) {
            HeapFree(GetProcessHeap(), 0, (LPVOID)ptu);
        }
        CloseHandle( hToken );
        CloseHandle( hProcess );
        return NULL;
    }

    LPWSTR stringSid;
    ConvertSidToStringSidW( ptu->User.Sid, &stringSid );
    jstring s = (*env)->NewString(env, (const jchar *)stringSid, (jsize)wcslen(stringSid));

    LocalFree(stringSid);

    if (ptu != NULL) {
        HeapFree(GetProcessHeap(), 0, (LPVOID)ptu);
    }
    CloseHandle( hToken );
    CloseHandle( hProcess );
    return s;
}

/*
 * Class:     com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl
 * Method:    getUserName0
 * Signature: (I)Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL
Java_com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl_getUserName0
  (JNIEnv *env, jclass winHelperClass, jint pid, jboolean prependDomain)
{
    HANDLE hProcess;

    hProcess = (pid == 0) ? GetCurrentProcess() : OpenProcess( PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid );
    if (NULL == hProcess)
        return NULL;

    HANDLE hToken = NULL;

    if( !OpenProcessToken( hProcess, TOKEN_QUERY, &hToken ) ) {
        CloseHandle( hProcess );
        return NULL;
    }

    DWORD dwSize = MAX_NAME;
    DWORD dwLength = 0;
    PTOKEN_USER ptu = NULL;

    if (!GetTokenInformation(
        hToken,         // handle to the access token
        TokenUser,      // get information about the token's groups
        (LPVOID) ptu,   // pointer to PTOKEN_USER buffer
        0,              // size of buffer
        &dwLength       // receives required buffer size
    )) {
        if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
            CloseHandle( hToken );
            CloseHandle( hProcess );
            return NULL;
        }

        ptu = (PTOKEN_USER)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwLength);

        if (ptu == NULL) {
            CloseHandle( hToken );
            CloseHandle( hProcess );
            return NULL;
        }
    }

    if (!GetTokenInformation(
         hToken,         // handle to the access token
         TokenUser,      // get information about the token's groups
         (LPVOID) ptu,   // pointer to PTOKEN_USER buffer
         dwLength,       // size of buffer
         &dwLength       // receives required buffer size
         )) {
        if (ptu != NULL) {
            HeapFree(GetProcessHeap(), 0, (LPVOID)ptu);
        }
        CloseHandle( hToken );
        CloseHandle( hProcess );
        return NULL;
    }

    SID_NAME_USE SidType;
    wchar_t lpName[MAX_NAME];
    wchar_t lpDomain[MAX_NAME*2 + 1];  // room for '\' + lpName
    jstring s = NULL;

    if( !LookupAccountSidW( NULL , ptu->User.Sid, lpName, &dwSize, lpDomain, &dwSize, &SidType ) )
    {
        DWORD dwResult = GetLastError();
        if( dwResult == ERROR_NONE_MAPPED )
           strcpy (lpName, "NONE_MAPPED" );
        else
        {
            printf("LookupAccountSid Error %u\n", GetLastError());
        }
    }
    else
    {
        if (prependDomain) {
            wcscat(lpDomain, L"\\");
            wcscat(lpDomain, lpName);
            s = (*env)->NewString(env, (const jchar *)lpDomain, (jsize)wcslen(lpDomain));
        }
        else {
            s = (*env)->NewString(env, (const jchar *)lpName, (jsize)wcslen(lpName));
        }
    }

    if (ptu != NULL) {
        HeapFree(GetProcessHeap(), 0, (LPVOID)ptu);
    }
    CloseHandle( hToken );
    CloseHandle( hProcess );
    return s;
}

/*
 * Class:     com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl
 * Method:    getEnvironment0
 * Signature: ()[Ljava/lang/String;
 */
JNIEXPORT jobjectArray JNICALL
Java_com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl_getEnvironment0
  (JNIEnv *env, jclass winHelperClass, jint pid)
{
    // TODO - implement this stub - not eay (have to open the process memory and poke around)
    // for now, return an empty array
    jobjectArray ret = (jobjectArray)(*env)->NewObjectArray(env, 0, (*env)->FindClass(env, "java/lang/String"), (*env)->NewStringUTF(env, ""));
    return ret;
}

static unsigned __int64 convertFileTimeToInt64( const FILETIME * pFileTime )
{
  ULARGE_INTEGER largeInt;

  largeInt.LowPart = pFileTime->dwLowDateTime;
  largeInt.HighPart = pFileTime->dwHighDateTime;

  return largeInt.QuadPart;
}

/*
 * Class:     com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl
 * Method:    getProcessMemoryInfo0
 * Signature: (I[J)V
 */
JNIEXPORT jboolean JNICALL Java_com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl_getProcessInfo0
  (JNIEnv *env, jclass winHelperClass, jint pid, jlongArray array)
{
    testLength(env, array, 4);

    HANDLE hProcess;
    PROCESS_MEMORY_COUNTERS pmc;

    hProcess = (pid == 0) ? GetCurrentProcess() : OpenProcess( PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid );
    if (NULL == hProcess)
        return FALSE;

    // Get the element pointer
    jlong* data = (*env)->GetLongArrayElements(env, array, 0);

    pmc.cb = sizeof(pmc);
    if ( GetProcessMemoryInfo( hProcess, &pmc, sizeof(pmc)) ) {
        data[0] = pmc.WorkingSetSize;
    }
    else {
        (*env)->ReleaseLongArrayElements(env, array, data, 0);
        return FALSE;
    }

    FILETIME creationTime;
    FILETIME exitTime;
    FILETIME kernelTime;
    FILETIME userTime;

    if ( GetProcessTimes( hProcess, &creationTime, &exitTime, &kernelTime, &userTime ) ) {
        // times returned from GetProcessTimes() are in 100-nanosecond units.
        data[1] = convertFileTimeToInt64(&userTime);
        data[2] = convertFileTimeToInt64(&kernelTime);
        data[3] = convertFileTimeToInt64(&creationTime);
        data[4] = 10000000; // 100 nanonseconds is this many ticks per second
    }

    CloseHandle(hProcess);

    (*env)->ReleaseLongArrayElements(env, array, data, 0);
    return TRUE;
}

/*
 * Class:     com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl
 * Method:    getProcessIOInfo0
 * Signature: (I[J)V
 */
JNIEXPORT jboolean JNICALL Java_com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl_getProcessIOInfo0
  (JNIEnv *env, jclass winHelperClass, jint pid, jlongArray array)
{
    testLength(env, array, 6);

    HANDLE hProcess;
    IO_COUNTERS iocounters;

    hProcess = (pid == 0) ? GetCurrentProcess() : OpenProcess( PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid );
    if (NULL == hProcess)
        return FALSE;

    BOOL rc = GetProcessIoCounters( hProcess, &iocounters );
    if (!rc) {
        CloseHandle(hProcess);
        return FALSE;
    }

    // Get the element pointer
    jlong* data = (*env)->GetLongArrayElements(env, array, 0);

    data[0] = iocounters.ReadOperationCount;
    data[1] = iocounters.WriteOperationCount;
    data[2] = iocounters.ReadTransferCount;
    data[3] = iocounters.WriteTransferCount;
    data[4] = iocounters.OtherOperationCount;
    data[5] = iocounters.OtherTransferCount;

    (*env)->ReleaseLongArrayElements(env, array, data, 0);
    CloseHandle(hProcess);
    return TRUE;
}


/*
 * Class:     com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl
 * Method:    getProcessHandle0
 * Signature: (I)J
 */
JNIEXPORT jlong JNICALL Java_com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl_getCurrentProcessHandle0
  (JNIEnv *env, jclass winHelperClass) {

    HANDLE hProcess;
    hProcess = GetCurrentProcess();
    return (jlong) hProcess;
}

/*
 * Class:     com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl
 * Method:    getProcessHandle0
 * Signature: (I)J
 */
JNIEXPORT jlong JNICALL Java_com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl_getProcessHandle0
  (JNIEnv *env, jclass winHelperClass, jint pid) {

    HANDLE hProcess = (pid == 0) ? GetCurrentProcess() : OpenProcess( PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid );
    return (jlong) hProcess;
}

/*
 * Class:     com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl
 * Method:    getLimitedProcessHandle0
 * Signature: (I)J
 */
JNIEXPORT jlong JNICALL Java_com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl_getLimitedProcessHandle0
  (JNIEnv *env, jclass winHelperClass, jint pid) {

    HANDLE hProcess = (pid == 0) ? GetCurrentProcess() : OpenProcess( PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid );
    return (jlong) hProcess;
}

/*
 * Class:     com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl
 * Method:    closeHandle0
 * Signature: (J)V
 */
JNIEXPORT void JNICALL Java_com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl_closeHandle0
  (JNIEnv *env, jclass winHelperClass, jlong handle){

    CloseHandle((HANDLE)handle);
}

/*
 * Class:     com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl
 * Method:    terminateProcess0
 * Signature: (IIB)V
 */
JNIEXPORT jboolean JNICALL Java_com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl_terminateProcess0
  (JNIEnv *env, jclass winHelperClass, jint pid, jint exitCode, jint waitMillis) {

    HANDLE hProcess = (pid == 0) ? GetCurrentProcess() : OpenProcess( PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid );
    if (!hProcess) {
        return FALSE;
    }
    TerminateProcess(hProcess, (unsigned int)exitCode);
    if (waitMillis >= 0) {
        WaitForSingleObject(hProcess, waitMillis);
    }
    CloseHandle(hProcess);
    return TRUE;
}