Link to home
Start Free TrialLog in
Avatar of InteractiveMind
InteractiveMindFlag for United Kingdom of Great Britain and Northern Ireland

asked on

JNI — UnsatisfiedLinkError

Here's my Java class:


public class CJ_CpuUsage
{
      private native int JCpuUsage() ;
      
      
      public CJ_CpuUsage()
      {
            int c = this.JCpuUsage() ;
            System.out.println( c+"%" ) ;
      }
      
      public static void main( String [] args )
      {
            new CJ_CpuUsage() ;
      }
      
      static
      {
            System.loadLibrary( "JCpuUsage" ) ;
      }
}


I've compiled a C++ DLL, called JCpuUsage -- with a CPP file and header called JCpuUsage, with the following function sig:

JNIEXPORT jint JNICALL Java_JCpuUsage_JCpuUsage
  (JNIEnv *, jobject);

The DLL is in the same directory as my class.

I've added both the DLL itself, and the current directory to the PATH variable, and have tried running it...

However, I keep getting the following error:

Exception in thread "main" java.lang.UnsatisfiedLinkError: JCpuUsage
      at CJ_CpuUsage.JCpuUsage(Native Method)
      at CJ_CpuUsage.<init>(CJ_CpuUsage.java:8)
      at CJ_CpuUsage.main(CJ_CpuUsage.java:14)

Line 8 obviously being:

    int c = this.JCpuUsage() ;


Any ideas why ?!

Thanks very much.
ASKER CERTIFIED SOLUTION
Avatar of CEHJ
CEHJ
Flag of United Kingdom of Great Britain and Northern Ireland image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of InteractiveMind

ASKER

Well, I've tried executing like so:

java -Djava.library.path=. CJ_CpuUsage

But same problem.
You can determine that property by

System.out.println(System.getProperty("java.library.path"));
new CJ_CpuUsage() ;
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
> java -Djava.library.path=. CJ_CpuUsage

and thats fine :)
Make sure your package structure is correct
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
>>Java_JCpuUsage_JCpuUsage

should be more like

Java_CJ_CpuUsage_JCpuUsage

i think, so make sure you have updated the native code
Have shoved it in a package - and it's made no difference.


Here's my JCpuUsage.h file:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class JCpuUsage */

#ifndef _Included_JCpuUsage
#define _Included_JCpuUsage
/*
 * Class:     JCpuUsage
 * Method:    JCpuUsage
 * Signature: ()I
 */
JNIEXPORT jint JNICALL Java_JCpuUsage_JCpuUsage
  (JNIEnv *, jobject);

#endif


And my JCpuUsage.cpp:

#include <jni.h>
#include "JCpuUsage.h"
#include "CpuUsage.h"

JNIEXPORT jint JNICALL Java_JCpuUsage_JCpuUsage(JNIEnv * env, jobject obj)
{
    //CCpuUsage cpu ;
    //int usage = cpu.GetCpuUsage() ;
    ////convert 'usage' to jint instance
    jint i = 0 ;
    return i ;
}
What's your package declaration?
package rob;

?
With that package, you should get something more like:


/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class rob_CJ_CpuUsage */

#ifndef _Included_rob_CJ_CpuUsage
#define _Included_rob_CJ_CpuUsage
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     rob_CJ_CpuUsage
 * Method:    JCpuUsage
 * Signature: ()I
 */
JNIEXPORT jint JNICALL Java_rob_CJ_1CpuUsage_JCpuUsage
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif
(Generated by the following command):

javah rob.CJ_CpuUsage
Okay, thanks very much — I will have to try this stuff tomorrow now .. am getting sleepy.  :(

Cheers so far.
> Okay, thanks very much — I will have to try this stuff tomorrow now .. am getting sleepy.  :(

see the link I posted above, it covers step by step whats needed to create the dll
Yay, after a good nights sleep, and starting from scrap (using a package, and giving more appropriate names to the classes etc to avoid confusion) it's interfacing nicely with the native code :)

It is strange however -- my CCpuUsage class (which is the native code that returns the actual CPU usage value) keeps returning 0; although it worked fine when I used it in a test EXE yesterday...

Any guesses as to why this is playing up? Could it be anything to do with JNI?

If not, then that's cool — I'll just bug jkr again ;)


Thanks :)
Well the last code you posted seemed to be largely commented out - apart from the bit that was returning zero ;-)
lol No no, I've edited that portion of the code, so that it does return what I want ;)

JNIEXPORT jint JNICALL Java_rob_OutputCpuUsage_CpuUsage( JNIEnv * env, jobject obj )
{
    CCpuUsage cpu ;
    jint usage = cpu.GetCpuUsage() ;
   
    printf( "From Library: %i%s\n", usage, "%" ) ;
   
    return usage ;
}


The output is:

From Library: 0%
Current CPU Usage: 0%
Incidentally, in case you're not aware, you can do this in pure Java >= 1.5:

http://java.sun.com/j2se/1.5.0/docs/api/java/lang/management/MemoryUsage.html
That's memory usage isn't it?
Well, it depends :) sometimes if the CPU usage is less than 1% (like 0.5%), it might return a 0. Can you try returning a double?
My CPU usage is definitely above 0.
>> That's memory usage isn't it?

Yes. Its not CPU utilization.
Above 0 but what it if it is less than 1? Is there a harm trying to make it a double?
Have tested -- it's definitely not returning some value below 0.

Besides, the GetCpuUsage() function that I'm calling doesn't even return that data type.. it should be returning an integer from 0-100.
>> printf( "From Library: %i%s\n", usage, "%" ) ;
>> From Library: 0%

Then maybe that part is worth: >> I'll just bug jkr again ;)
>> Have tested -- it's definitely not returning some value below 0.

But that's not what the printf () prints?
What do you mean?
I mean the printf () also prints 0%, right? So with what did you test that you got a value different than 0?
What does the C++ code return when it is executed directly?
Can you show the native code?
wowowowow...

You can retrieve the CPU usage (as a percentage) using pure Java ?!

Okay, I can get the CPU time:

long cpu_time = ManagementFactory.getThreadMXBean().getCurrentThreadCpuTime() ;

How to turn this into a percentage of use?
>>How to turn this into a percentage of use?

Well that's a good question ;-) And I don't know the answer
I don't think Java lets you get the CPU usage directly because there could be multiple processors. All you can get is the number of processors you have (Runtime.getRuntime ().getAvailableProcessors () or something similar), and the time associated with each thread....
I've tried existing ones (including that one from JavaWorld), but they never compile for me... I don't know if my IDE just doesn't like them, or what..

So, jkr helped me adapt an existing one online yesterday, such that it does compile fine (and work) in my IDE, Dev-C++.


Hmm, uh oh...

I just tested my CCpuUsage class using some native code (compiled and ran as an EXE), and it's returning 0 as well... :(

That's really strange. That class was working fine yesterday...


I think I'll go bug jkr then....  :-\
>> and it's returning 0 as well...
>> That class was working fine yesterday...

Maybe your CPU utilization has gone down ;-) he he.... try keeping some 100 applications on and then try :)
same prob
Try on another machine.
Check your arithmetic in your native code and make sure it's  using types large enough to hold the large numbers involved or you may get overflow
Okay, I just went through the C++ code, and think I've found what the problem is (but am not sure how to fix it)...

Here's the function that I'm calling (which in turn calls other functions):


int CCpuUsage::GetCpuUsage()
{
      static PLATFORM Platform = GetPlatform();

      if (m_bFirstTime)
            EnablePerformaceCounters();
      
      // Cpu usage counter is 8 byte length.
      CPerfCounters<LONGLONG> PerfCounters;
      char szInstance[256] = {0};

//            Note:
//            ====
//            On windows NT, cpu usage counter is '% Total processor time'
//            under 'System' object. However, in Win2K/XP Microsoft moved
//            that counter to '% processor time' under '_Total' instance
//            of 'Processor' object.
//            Read 'INFO: Percent Total Performance Counter Changes on Windows 2000'
//            Q259390 in MSDN.

      DWORD dwObjectIndex;
      DWORD dwCpuUsageIndex;
      switch (Platform)
      {
      case WINNT:
            dwObjectIndex = SYSTEM_OBJECT_INDEX;
            dwCpuUsageIndex = TOTAL_PROCESSOR_TIME_COUNTER_INDEX;
            break;
      case WIN2K_XP:
            dwObjectIndex = PROCESSOR_OBJECT_INDEX;
            dwCpuUsageIndex = PROCESSOR_TIME_COUNTER_INDEX;
            strcpy(szInstance,"_Total");
            break;
      default:
            return -1;
      }

      int                        CpuUsage = 0;
      LONGLONG            lnNewValue = 0;
      PPERF_DATA_BLOCK pPerfData = NULL;
      LARGE_INTEGER      NewPerfTime100nSec = {0};
   
      lnNewValue = PerfCounters.GetCounterValue(&pPerfData, dwObjectIndex, dwCpuUsageIndex, szInstance);
      NewPerfTime100nSec = pPerfData->PerfTime100nSec;

      if (m_bFirstTime)
      {
            m_bFirstTime = false;
            m_lnOldValue = lnNewValue;
            m_OldPerfTime100nSec = NewPerfTime100nSec;
            return 0;
      }
   
      LONGLONG lnValueDelta = lnNewValue - m_lnOldValue;
      double DeltaPerfTime100nSec = (double)NewPerfTime100nSec.QuadPart - (double)m_OldPerfTime100nSec.QuadPart;

      m_lnOldValue = lnNewValue;
      m_OldPerfTime100nSec = NewPerfTime100nSec;

      double a = (double)lnValueDelta / DeltaPerfTime100nSec;

      double f = (1.0 - a) * 100.0;
      CpuUsage = (int)(f + 0.5);      // rounding the result
      
      if (CpuUsage < 0)
            return 0;
      return CpuUsage;
}


Take note to this section:

      if (m_bFirstTime)
      {
            m_bFirstTime = false;
            m_lnOldValue = lnNewValue;
            m_OldPerfTime100nSec = NewPerfTime100nSec;
            return 0;
      }

It doesn't seem to get past this -- it would seem that m_bFirstTime is true all the time... it is initialized to true when I create an instance of the CCpuUsage class. It only seems to be set to false on the second call of that function.

So, I thought that I'd change my function to:

JNIEXPORT jint JNICALL Java_rob_OutputCpuUsage_CpuUsage( JNIEnv * env, jobject obj )
{
    CCpuUsage cpu ;
    jint usage = cpu.GetCpuUsage() ;
    usage = cpu.GetCpuUsage() ;        // Call GetCpuUsage() a second time
   
    printf( "From Library: %i%s\n", usage, "%" ) ;
   
    return usage ;
}

When I run this several times, it _sometimes_ output 100% -- but still, most of the time, it's still 0%.

Any ideas?

Thanks
Well, I bugged Jkr, and it turned out that the interval between the first call, and the second call of the native function was not large enough — so, he worked his magic, and added some struct etc etc, and somehow, it all works fine now !

Thanks for all the help with the initial problem guys — points to objects and CEHJ  =)
The API documentsexplain that
Sorry - wrong Q ;-)
glad to help :)
:-)