Welcome to MSDN Blogs Sign in | Join | Help

Hopper: Start Menu Dead!

Interesting post over at "Reed & Steve" regarding Hopper and full screen apps and the Start Menu Dead message. Check it out here. Kudos to them for making this available.
Posted by shende | 3 Comments

Use CallWindowProc when using WNDPROC pointers directly

 

A recurring theme I see while debugging application compatibility issues has to do with the direct use of the window proc pointer. If the intended WNDPROC exists in a DLL that is located in Slot 0, the pointer “looks right” and is often mistakenly used directly. In Windows Mobile, direct access to the window procedure requires you go through CallWindowProc().

 

Instead of trying to use the pointer directly, use CallWindowProc() like this:

 

// Direct call to wndProc()

// Incorrect! tp.result = recvWndClass.lpfnWndProc(NULL, GCI_WNDPROC_TEST_MSG,NULL,NULL);

tp.result = CallWindowProc(recvWndClass.lpfnWndProc, NULL, GCI_WNDPROC_TEST_MSG,NULL,NULL); // Good Job!!

 

The reason for this indirection has to do with the way proc addresses are tracked by GWE and “mapped” to different processes. Sometimes you can get “lucky” and these addresses will work – but if the DLL you are trying to access is loaded outside of Slot 0, this mapping will break and your application will crash. Using CallWindowProc() abstracts this mapping for you so you don’t have to worry about it. Please see the WM documentation for Get/SetWindowLong() as well as other WM WNDPROC API’s for more information.

Posted by shende | 1 Comments

Are you passionate about Windows Mobile Devices?

Do you want to work on next generation Windows Mobile devices long before they become commercially available? Want to be the first one to work with and influence next year’s Windows Mobile devices? Then you’re in luck! The Windows Mobile team is looking for a strong developer who is passionate about the next wave of our devices.

The Windows Device Core Joint Development Program (JDP) team is looking for an experienced developer to assist OEMs and Silicon Vendors in putting the latest Windows Mobile on both new and existing hardware. We are looking for passionate, smart, and motivated candidates, with great development, problem solving, and debugging skills, ready to meet the highest standard of engineering excellence.

 

Find out more about it here.

Posted by shende | 1 Comments

MemoRx incorrectly displaying VM overlap in pre-release Windows Mobile versions

Many OEM’s have already noticed that Memory Doctor doesn’t correctly represent VM overlap in some pre-release versions of Windows Mobile. There have been some changes in WM VM architecture that contradict an assumption made by MemoRx which results in potential incorrect reporting. MemoRx will continue to work well in officially release versions of Windows Mobile. 

 

Instead of updating MemoRx and continuing with our 2-tool approach, we though it would be more useful to merge the useful features of MemoRx with devHealth to create the devHealthViewer. We are currently working on the documentation as well as some additional features to this new tool, but I can tell you it is very impressive and useful. You can download a version of this tool today from Jetstream under the WM6 Tools directory.

Posted by shende | 8 Comments

Understanding Output From “meminfo kernel”

I was debugging a weird hang at device boot and I used the command “meminfo kernel” in CeDebugX to get more info, but I realized right away that I didn’t know what this command was showing me.  So, I did a bit of investigation into what the output meant and thought I’d write down what I learned so I’d remember it later.  And, of course, there’s no better place to write things down than on the HopperRx blog, so here’s what I learned.

Intro to Kernel Heap

To understand the output of "meminfo kernel", I need to explain a bit about the kernel's internal heap.  As with all heaps, the kernel's heap allocates a page of memory and then divides that memory into blocks of various sizes as needed.  The kernel uses the following 8 different block sizes (in bytes): 16, 36, 64, 168, 228, 524, 576, 1024.  Each block size directly corresponds to the actual size of at least one kernel data structure.  To demonstrate this, I used a handy trick in PB's Watch Window to show the structure of the kernel's EVENT data type (see image below).  If you add up all the bytes in the structure (pProxHash has an array size of 32) you'll see that sizeof(EVENT) is 168 bytes, which fits perfectly into the bucket that is 168 bytes.  It turns out that all of the kernel's data types exactly matches one of the 8 buckets.LPEVENT structure, from the Watch Window

Unlike the heap used by LocalAlloc(), the kernel's private heap doesn't do compaction.  This means that when a chunk of memory is allocated as a certain block, it stays that size until the device reboots.  The block will be marked as free when the object it contains is deleted and could be re-used by another object later on, but the blocks will never be resized.  If the kernel needs to create a new object but no empty block exists, it will simply create a new block.  Consequently, the kernel heap will grow as necessary, but never shrink.  This has some important implications I'll touch on later.

Dissecting "Meminfo Kernel"

Okay, that's the basic stuff on the heap, now let's look at what meminfo shows.  Here's the output I got from meminfo on DeviceEmulator after a clean boot:
------------------------------------------------------------------

      Size     Used      Max   Extra   Entries(Max)   Name
==================================================================
 0:    576    98496    99648    1152    171(   173)   Thrd
 1:    228    54036    54948     912    237(   241)   Mod
 2:     36    91944    92412     468   2554(  2567)   API/CStk/ClnEvt/StbEvt/Prxy/HData/KMod
 3:    168   598920   604800    5880   3565(  3600)   Crit/Evt/Sem/Mut/ThrdDbg
 4:     64    33920    34048     128    530(   532)   FullRef/FSMap/ThrdTm
 5:     16    32864    33120     256   2054(  2070)   MemBlock
 6:    524        0     1572    1572      0(     3)   Name
 7:   1024   174080   175104    1024    170(   171)   HlprStk
 
Total Used  =   1084260 bytes
Total Extra =     11392 bytes
heapptr: 0x80076278
 
------------------------------------------------------------------
Here's my quick break down of what each column is showing:
First column:  The left most column is just a number for the table row.  The block sizes aren't listed in any special order so the numbering doesn't mean anything special, but the line numbers make the table easier to read and discuss.
Size:  The size, in bytes, of the block described on that particular row.
Used:  Number of bytes currently in use at this particular size (i.e. "Used" = "Size" * "Entries")
Max:  Number of bytes allocated at this size (i.e. "Max" = "Size" * "(Max)")
Extra:  Number of bytes that aren't being used (i.e. ""Extra" = "Size" * "((Max) - Entries)).
Entries:  Number of blocks of this size that are currently in use.
(Max):  Number of blocks of this size that have been created.
Name:  Friendly names of the data types that fit into this block
 
At the bottom of the "Total Used" shows the sum of all the "Used" columns.
The "Total Extra" shows the sum of all the of all the "Extra" columns.  
And, the "heapptr" is just what is sounds like, the pointer to the start of kernel's heap.

What the output actually means

In my table above, row 0 shows the blocks of size  576 bytes.  There are currently 173 of these blocks created, and 171 of those blocks are in use.  That means, 99,648 bytes are allocated as this block size, and  98,496 of those bytes are in use.   This particular block is only used to hold THREAD objects, so this information also tells us there are currently 171 threads in the system.

Contrary to the implications of their names, the Max and (Max) columns don't reflect any kind of upper limit.  These columns are actually reporting the number of blocks of that size that have been created.  Going back to row 0 as an example again, the Max column shows that 173 blocks have been created at size 576.  However, when I took this snapshot, only 171 threads were still around, so only 171 of those blocks were used.  If a new application was started and created 3 new threads, the kernel would re-use the 2 empty blocks for 2 of the new threads, and then create a new block for the other new thread.  After this happened, "Entries" and "(Max)" would both be match at 174, indicating there are 174 blocks for that size and all 174 of those blocks are in use.

How To use the output

This table is a great way to get an idea of where to start a new investigation.  It doesn't provide much help with root causing a specific issue though, so don't try and read too much into the information. 

For example, if the table shows rows 2 and 3 have a huge number of entries, then some process is probably leaking handles to a critical section, event, semaphore, or mutex.  When one of these objects is created, the object stays around until all handles are closed.  If the handle is leaked, then the handle (block size 36) and the object (block size 168) are both going to stay around and never get deleted.  On the other hand, if row 2 has a huge number of entries but all the other blocks have a normal number of entries, then it's possible someone is leaking a thread or module handle.  Since threads exit on their own, and there's only one instance of a DLL, neither of these buckets will grow even though there are a bunch of handles pointing at them.  If all of the columns look fine, then the issue probably isn't a handle leak or object. 

As you can see, this info can provide a helpful guide on where to start debugging, which is very valuable sometimes, but it doesn't tell you anything specific about the root cause.

Posted by JeCahill | 2 Comments
Attachment(s): WatchWindow_Snapshot.jpg

Passive KITL to the rescue

I'm sure many of you have been in a situation where your device hangs during field testing. Or sometimes you are trying to track down a problem which only repros at a certain location. The best thing you can have in these situations is, of course, a live KITL connection to the device. This can be challenging at times because you can't run around with a phone connected to your laptop with a short USB cable. Its just not practical.

This is where Passive KITL can be used. (Read the “Active and Passive KITL” article in the Windows Mobil 6.0 documentation for an introduction to Passive KITL.) Note that, when you don't have live KITL connection, you can't just press "go" when your device tried to break into KITL. This might happen, for example, when you mistakenly use a wrong Debug DLL with a lot of asserts you don't care about. So a word of advice, use only debug dlls for the components you are debugging at the moment. Better yet, try to stay with a retail image this way if it breaks it will be most likely in the place you are debugging.

Another thing to remember is battery life. You need to act quickly when your device breaks. If you let it sit too long your battery might run out and you will lose your repro.

Yet another thing you can do before working with Passive KITL is to use non-optimized version of the components you are trying to debug. Use the -Od switch in your SOURCES file to turn off Compiler optimization. This will turn off the optimization and preserve with assembly flow which in turn will make your assembly match your sources files. You can add -Od to the existing command line by doing this:

 

 

    CDEFINES=$(CDEFINES) -Od

 

 

Alternatively, you can set DISABLE_OPTIMIZER=1 in your build environment which will disable  optimization

 

No really, how do I enable Passive KITL?

We are going to use the GSample platform that can be downloaded from JetStream or you can just use FSample. Your particular platform will be very similar.

In a nutshell enabling Passive KITL is very simple. All you need to do is to OR OAL_KITL_FLAGS_ENABLED and OAL_KITL_FLAGS_PASSIVE from \PLATFORM\COMMON\SRC\INC\oal_kitl.h into OAL_KITL_ARGS->flags in the OALKitlStart() function.

There are several ways to do this. If you have a boot-loader menu you can do it there (We will not be describing it here, because not everyone has a boot-loader menu).

Another way is to use FIXUPVARs.Which can be configured so that all you need to do is to set a DOS environment variable and run MAKEIMG to make an image with Passive KITL enabled.

Take a look at the CONFIG.BIB file located in your \PLATFORM\<YOUR PLATFORM>\FILES directory or look at the sample (\PLATFORM\GSAMPLE\FILES\CONFIG.BIB).

You will see a section that looks something like this:

 

 

    ARGS                                  87AFF800    00000800    RESERVED
    GSM                                   87C00000    00400000    RESERVED
    dwOEMFailPowerPaging    00000000    00000001    FIXUPVAR
    dwOEMDrWatsonSize        00000000    0004B000    FIXUPVAR
    dwOEMPagingPoolSize      00000000    00200000    FIXUPVAR

You can add a new value here that is conditional on a makeimg flag:

IF IMGPASSIVEKITL 
    dwOEMEnablePassiveKITL  00000000    00000001    FIXUPVAR
ENDIF IMGPASSIVEKITL

The value dwOEMEnablePassiveKITL should live in \PLATFORM\GSAMPLE\SRC\KERNEL\OAL\init.c .  If you take a look at that file you will see something similar to this:  

 

    //---------------------------------------------------------
    // Global FixUp variables
    //
    // Note: This is workaround for makeimg limitation 
    //  no  fixup on variables
    // initialized to zero.
    //

    DWORD dwOEMFailPowerPaging = 1;
    DWORD dwOEMDrWatsonSize = 0x0004B000;
    DWORD dwOEMPagingPoolSize = 0x00200000;
    DWORD dwOEMPlatform = OAL_IOCTL_PLATFORM_WINCE;

    extern DWORD gdwFailPowerPaging;
    extern DWORD cbNKPagingPoolSize;
    extern LPCWSTR g_pPlatformManufacturer;
    extern LPCWSTR g_pPlatformName; 

 

You need to add the actual value that will be modified in the makeimg process.  In the example above, that would be:

DWORD dwOEMEnablePassiveKITL = 0xFFFFFFFF;
extern DWORD g_dwEnablePassiveKITL;

Why is this initialized to 0xFFFFFFFF and not 0x0?  Because you cannot have fixup variables initialized to 0x0 (See the comments in the init.c file above).

The extern in front of g_dwEnablePassiveKITL is important because this global variable is used in this file but is declared and initialized in kitl.c. 

Next, you need to copy the FIXUPVAR dwOEMEnablePassiveKITL to g_dwEnablePassiveKITL in OEMInit(). Search for OEMInit() in init.c and add this line somewhere in the function:

g_dwEnablePassiveKITL = dwOEMEnablePassiveKITL;

In order to make use to the new FIXUPVAR, you need to modify \PLATFORM\GSAMPLE\SRC\kernel\oal\kitl.c.  First, you must define and initialize the global at the top of kitl.c:

DWORD g_dwEnablePassiveKITL = 0xFFFFFFFF;

NOTE: this global must be defined in both KITL and non-KITL builds or else you will not be able to build properly.  In the case of non-KITL, it will be ignored.   If you separate your KITL functions for non-KITL builds into a separate file with empty stubs for the KITL functions, make sure you also declare this global there.  In our case this is done in \PLATFORM\GSAMPLE\SRC\kernel\oal\kitl.c.

Finnaly, in OALKitlStart(), you need to add the flag.  You probably call OALArgsQuery() to get the current value.  If you set this value in the bootloader, this is how it is passed in.  Once you have a pointer to OAL_KITL_ARGS (we will call it pArgs in this example) you need to OR in OAL_KITL_FLAGS_PASSIVE:

 

if (0xFFFFFFFF != g_dwEnablePassiveKITL)
{
      pArgs->flags |= OAL_KITL_FLAGS_PASSIVE;
}

 

That is it.  Now, after building, you can enable passive KITL by setting IMGPASSIVEKITL=1 in your build environment and running makeimg. 

 

Let's recap

1. Make sure you only use debug dlls that are relevant
2. Plug the device into a PC as quickly as possible when the device hits an exception and KITL is activated.  Otherwise, the battery will drain and the crash will be lost
3. Turn off Compiler Optimization to the components you are debugging if possible
4. Add a FIXUPVAR in config.bib
5. Copy value of FIXUPVAR to the global in init.c
6. OR OAL_KITL_FLAGS_PASSIVE to pArgs->flags in kitl.c if your global (g_dwEnablePassiveKITL) is set 

Posted by deanmel | 3 Comments
Filed under: , , ,

Running Platform Builder 6 on Vista

There are a lot of people that afraid to switch of Vista because they are afraid that their stuff will not work. Well truth be told, I've been running Vista+VS2005_SP1+PB6 since March of this year and haven't had any major problems. The only two problems I had was:

1. If the device doesn't have Vista compatible driver then you will not be able to connect/debug the device.

2. Flashing the devices on Vista can be painful because some devices still have no support for Vista flashing.

In order to have this properly working you need to uninstall PB5 from your machine before installing PB6. Personally, I would use a fresh Vista install.

After you install Vista and Visual Studio 2005 on you development computer, you need to install VS2005_SP1 which can be found here:

http://www.microsoft.com/downloads/details.aspx?familyid=bb4a75ab-e2d4-4c96-b39d-37baf6b5b1dc&displaylang=en

Ignore all the warnings that Vista will give you about VS2005 being incompatible with Vista.

Install Vista update for VS2005_SP1 from here:

http://www.microsoft.com/downloads/details.aspx?familyid=90E2942D-3AD1-4873-A2EE-4ACC0AACE5B6&displaylang=en

Now you are finally ready to install PB6 and PB6_SP1. Follow this link to install PB6:

http://www.microsoft.com/downloads/details.aspx?familyid=5733C26C-168B-474D-8A27-59B30B769402&displaylang=en

and

PB6_SP1

http://www.microsoft.com/downloads/details.aspx?FamilyId=BF0DC0E3-8575-4860-A8E3-290ADF242678&displaylang=en

 

Conclusion

I know this might seem like a lot of work just to install PB6 but trust me the benefits that you will get from using PB6 will outweigh this cumbersome  install. Just the ability to use mismatched PDB files does it for me. And also full integration with Visual Studio is a very nice addition.

Posted by deanmel | 1 Comments
Filed under: ,

Improving the Cat Parade (Part 3)

I was recently running hopper on a device that supported screen rotation and I realized that my test coverage was completely missing the rotation scenario.  The device would switch between portrait and landscape mode if the user took a specific action, but Hopper is automated and limited to standard key presses, so it would never take this action.

Once again, I extended FocusApp a little to improve my test coverage.  When FocusApp starts, I have it create a thread that calls ChangeDisplaySettingsEx() at random intervals to flip the orientation between different angles. 

The original sources for FocusApp are here: http://blogs.msdn.com/hopperx/archive/2005/11/30/the-cat-parade.aspx

To give you an idea of how I implemented the rotation, here's a bit of sample of the code:

...
    DEVMODE devMode;
...
Inside a while loop:
...
        //Wait for a random number of minutes before rotating the screen.

        //Reinitialize the structure.
        memset(&devMode, 0x00, sizeof(devMode));
        devMode.dmSize = sizeof(devMode);
        devMode.dmFields = DM_DISPLAYORIENTATION;

        //Figure out which orientation should be used this time.
        if (DMDO_0 == devMode.dmDisplayOrientation)
        {
            //Currently at 0 degrees, so flip to 90 degress.
            devMode.dmDisplayOrientation = DMDO_90;
        }

        else
        {
            //Currently at 90 degrees, so flip back to 0 degrees.
            devMode.dmDisplayOrientation = DMDO_0;
        }

        //Now apply the new orientation to the screen.
        ChangeDisplaySettingsEx(NULL, &devMode, NULL, CDS_RESET, NULL);
...

Not all devices use the orientations DMDO_0 or DMDO_90 and some device support more than two orientations, so, modify the code to alternate between all of the orientations your test devices supports.  Also, the interval between rotations is pretty much arbitrary, but it's important to make the interval random and variable to get good coverage.  In my testing, the rotation is done on a separate thread that randomly sleeps for 1 to 30 minutes each time through the loop.

Posted by JeCahill | 2 Comments
Filed under: , , ,

Where did Callstacks go from the Hopper logs?

If you upgraded from an older version of Hopper to a more recent one, you probably noticed that the callstacks are gone from the Hopper logs.

We made them optional for two reasons:

1. Printing out callstacks slowed down the run significantly

2. They did not provide a lot of value, because the callstacks were printed out at 5 minute intervals and not at the points of interest

If you feel that you can benefit from these callstacks you can turn them back on by passing /d to Hopper.exe. To pass a switch to hopper you can use the debugger's target window. If you don't have a debugger attached you can create a link file that passes the switch to Hopper.exe. MSDN has instructions on how to create link files here

Posted by deanmel | 3 Comments
Filed under: ,

Why my private binaries do not show up in the image?

Why my private binaries do not show up in the image? I've been asked this question too many times by now. Many partners when testing their private changes have to figure it out the hard way.

The reason why your updated binary doesn't show up in the image is because by default make image picks up package files from %_FLATRELEASEDIR%\prebuilt directory. All you need to do is to figure out which package your dll/exe goes to and delete that package. Here are the steps I do whenever I need to update a dll in an image.

First you need to find which package your dll goes into. There are several way to do that.

So here is what you need to do:

  1. Copy your binary, symbols, map into %_FLATRELEASEDIR%
  2. Find out what package your file is in
    1. Cd %_FLATRELEASEDIR%
    2. "findstr /i [your filename] *.bsm.xml" Ex: findstr /i nk.exe *.bsm.xml
  3. This should return a package's xml file, for example oemxipkernel.bsm.xml. This tells you that it's in the OS package.
  4. Find the prebuilt package and delete it
    1. cd prebuilt
    2. del oemxipkernel.cab.pkg
  5. Makeimg

Makeimg will detect the missing package and rebuild it.

Posted by deanmel | 4 Comments
Filed under: ,

Improving the Cat Parade (Part 2)

There have been a few blogs that talk about the benefit of focusing hopper runs on individual applications to work out the stability bugs one application at a time.  But, there comes a time when testing one by one may not be the best use of resources.  Hopper runs are resource intensive (up to 10 devices running pass after pass for days at a time) and if you add several applications into an image, the testing matrics and time requirements become pretty overwhelming.

 

But, no worries.  FocusApp is still the right solution for your stability testing needs.  Instead of having FocusApp focus on one application at a time, have it iterate between a few of the applications.  This will give you less coverage over each individual application, but it gives you the flexibility to balance hopper's resource usage.

The original source for FocusApp is available here:  http://blogs.msdn.com/hopperx/archive/2005/11/30/the-cat-parade.aspx

And, here's some quick sample code that shows how I modified the sources to iterate through a list of apps:

 

WCHAR *g_aAppList[] = {
    {L"\\Windows\\BubbleBreaker.exe"},
    {L"\\Windows\\Solitare.exe"},
    {L"\\Windows\\WMPlayer.exe"},
    {L"\\Windows\\WMPlayer.exe"},
    {L"\n"} //This marks the end of my list
};

 

WCHAR *g_aParamList[] = {
    {NULL}, //No parameter for bubblebreaker
    {NULL}, //No parameter for solitare
    {L"\\My Documents\\My Music\\Really Long Sample.MP3"},
    {L"\\My Documents\\My Music\\Another Long Sample.3GP"},
    {L"\n"} //Indicate this is the end of the list.
};

 

...

 

    PROCESS_INFORMATION procInfo;
    int indx = 0;

 

...

 

        CreateProcess(
            g_aAppList[indx],   //Launch the next app in the list.
            g_aParamList[indx], //Pass the app some parameters.
            NULL,
            NULL,
            FALSE,
            NULL,
            NULL,
            NULL,
            NULL,
            &procInfo
            );

 

        //Increment the count so the next app gets launch in the next iteration.
        indx++;
        if L"\n" == g_aAppList[indx])
        {
            //We've reached the end of the list, loop back to the first app.
            indx = 0;
        }
    }

...

 

Notice that I have WMPlayer listed twice in g_aAppList.  I did this to show you can launch the same application with different parameters to test different features/functionality.  In this particular case, FocusApp will cause WMPlayer to load an MP3 file on one iteration, then to load a 3GP file on the next iteration.  This guarantees that hopper will provide some coverage over both these CODECs.  Of course, if different parameters don't change your app's behavior, then there's no need to pass it any parameters.  In this case, just delete all references to g_aParamList and pass NULL as the second parameter for CreateProcess().

Posted by JeCahill | 0 Comments

How to load mismatched PDBs in Platform Builder

I don't know how many times I wished Platform Builder had an option to load PDB files with timestamps that do not match my executable. This option was available for desktop users for decades. Windbg and other desktop debuggers can do it without problems. Mobile users didn't have that ability until recently.

Platform Builder 6 added an option which allows you to ignore timestamps. So if you haven't upgraded to PB6 this is a good reason to do it now.

Assuming you have PB6 installed, go to Tools\Options. Click on Platform Builder CE node, then System Debugger\General. You will see "Ignore Timestamp mismatches" in the right lower corner. See picture below. Check that option and click OK.

image

NOTE:

There is a small drawback, which I'm sure you already know about. If a PDB you are using is out of sync with your code or executable the lines will not match and you will be looking at the wrong code. So long as you keep that in mind, this option will prove to be a very useful many-many times over.

Using The Radio Interface Layer

 

I was recently talking with a colleague about the RIL Proxy and it occurred to me that this component isn't well known and is pretty confusing.  I've been digging into the RIL Proxy for a little while now so I've become pretty comfortable with it, so I thought I'd write a blog that introduces RIL Proxy, then build up to more in-depth blogs on how to debug radio problems using RIL Proxy logging.  This first article is just the high level introduction targeted at people who may have heard of RIL Proxy but haven't worked with it.

 

Overview of RIL

The Radio Interface Layer (RIL) is an abstraction layer that enables clients to interact with the cellular radio on Windows Mobile (WM) devices.  The RIL is split into two main components:  the RIL Proxy (Ril.dll), and the RIL Driver (he filename of the driver changes from BSP to BSP, but the default name is RilGsm.dll or RilCdma.dll).  The RIL Driver is device specific, so clients should avoid calling directly into the driver.  The RIL Proxy layer sits on top of the RIL Driver and provides standard APIs that a client can use to interact with the hardware.

 

The RIL Proxy is a pass through layer who's main jobs are to provide notifications of cellular radio changes and coordinate cellular radio related requests from multiple clients.  Since the it's just a pass through layer, the RIL Proxy doesn't store any state information about the radio or maintain any hardware configurations.  There are several components that sit on top of RIL Proxy and abstract radio functionality even further to simplify specific tasks.  For example, the SIM Manager provides APIs for reading and writing contact information from/to a SIM card.  However, these components are outside the scope of this document; for more information on the components built on top of the RIL Proxy, consult the "RIL Architecture" section of the Windows Mobile 6.0 documentation.

RIL Architecture

 RIL Proxy is a notification based component.  To enable RIL Proxy to communicate with an application, each application must register with the RIL Proxy by calling RIL_Initialize().  This API creates a unique instance of a RIL client which the app can interact with directly.  An application can create multiple RIL clients by calling RIL_Initialize() multiple times, but there's no real benefit to doing this so it's rarely done.   When a RIL client is initialized, the application passes the client two callback functions and subscribes to specific classes of notifications.

 

Notification Callback

The first callback function registered with RIL Proxy client is used to receive and process unsolicited notifications.  These notifications are generated by the RIL Proxy when the radio or cellular states change.  A notification for a particular state change is only sent to a client if the client has subscribed to receive that class of notifications.  For example, if the client has registered to receive the RIL_NCLASS_NETWORK class of notifications, then it will receive the notification RIL_NOTIFY_REGSTATUSCHANGED  when the cellular radio registers or deregisters on a cellular network.

 

Async Results Callback

The second callback function registered with the client is response callback.  This callback is used to asynchronously pass data to the application.  A majority of the functions exported by the RIL Proxy are asynchronous, so no actual data is returned by the API.  Instead, the API queues a command with the RIL Driver and returns a command ID.  After the command has been processed by the driver, the response callback function is called and given the HRESULT of the command, the command ID, and any requested data.  Apps can have multiple commands outstanding and they can differentiate between the responses by matching the command ID for each response, but, for simplicity, most apps issue RIL requests serially so the command ID isn't necessary in these cases.

Posted by JeCahill | 5 Comments
Filed under: , , ,

Attachment(s): Untitled picture.png

Saving VM by using OEMDRIVERSHIGH package

The release of Windows Mobile 6 (WM6) gave us a few more tools for dealing with Virtual Memory (VM) issues. Compaction of slot 1 is new for WM6, in previous releases all slot 1 modules were aligned on 64k boundaries, causing VM address space to be wasted when modules sizes aren’t perfectly divisible by the alignment size of 64KB were mapped into slot 1. The new alignment size of 4k allows more modules to be packed closer together, thus better compaction.

To illustrate this point, Figure 1 below shows 4 files totaling 142k in size taking up 320k using 64k alignment. Figure 2 shows the same 4 files only taking up 144k using 4k alignment. The white space is unused memory.

Along with compaction, the WM6 release also provides a way to map OEM driver’s modules into slot 1 with the package OEMDRIVERSHIGH. This is important because all the modules that do not fit into slot 1 rollover into slot 0, and thus reducing available VM for all slots. Further exacerbating the situation of modules rolling over into slot 0 is the fact slot 0 aligns modules on 64k boundaries. The OEMDRIVERSHIGH package enables OEMs, SVs, and system integrators to change the packaging priority of OEM drivers, which is typically near the end in the list of packages processed by makeimg.

When it comes to reconfiguring modules, OEM’s don’t have the ability to rearrange packages or to change the modules that go into a particular package. All that is available at this stage of building an image is reconfiguring the modules that go into the OEMDRIVERS package. Now with the addition of OEMDRIVERSHIGH package an OEM can target selected DLL’s to be processed, which will push larger modules out of slot 1 and into slot 0.

 

Optimizing OEMDRIVERSHIGH usage

To save VM using OEMDRIVERSHIGH we are going to identify the modules at the bottom of slot 1 on the verge of rolling over into slot 0. Through careful comparison of these modules and those currently in the OEMDRIVERS package we will reassign OEMDRIVERS modules to OEMDRIVERHIGH. By doing so the modules in OEMDRIVERSHIGH will be processed before the modules at the bottom of slot 1, and through this careful calculation the OEMDRIVERSHIGH modules targeted for slot 1 will rollover files on the slot 1 / 0 boundary. The VM savings will come from rolling over larger modules that are 100’s of KB into slot 0 which is 64KB align and filling the freed slot 1 space with smaller modules. It is the relocation, for example, of three 16KB modules to slot 1 that will free 192KB in slot 0 and only consume 48KB in slot 1.

To achieve this, the first place to start is analyzing the output of makeimg from a baseline build. The output isn’t always captured so you many need to run it once capturing the output (makeimg > makeimg.out 2>&1). Search through makeimg.out and look for “fixing up”. These are modules rolling over from slot 1 into slot 0. Below is an example of modules that just made it into slot 1 and modules that started rolling over into slot 0. This list of modules will be different for every project based on SYSGEN, BSP flags, IMG flags and the type of image.

Using the data from makeimg.out we will build a table to calculate the size of each module sitting at the bottom of slot1. Later we will use this data to determine how many modules to move into OEMDRIVERSHIGH, which will cause these modules to rollover.

 

                                                        FIGURE 3

 

Module cespell.dll          at offset 01d8a000 data, 0210b000 code

Module MsgStore.dll         at offset 01d89000 data, 02106000 code

Module msim.dll             at offset 01d87000 data, 020e4000 code

Module mscoree.dll          at offset 01d86000 data, 020d4000 code

Module netcfagl2_0.dll      at offset 01d85000 data, 02099000 code

Module netcfd3dm2_0.dll     at offset 01d84000 data, 02072000 code

Module ccoredrv.dll         at offset 01d83000 data, 02051000 code

Module ccoreutl.dll         at offset 01d82000 data, 02042000 code

Module cspvoice.dll         at offset 01d81000 data, 02032000 code

Module simsec.dll           at offset 01d80000 data, 02026000 code

Module sms.dll              at offset 01d7f000 data, 02020000 code

Code space full, fixing up supsvcs.dll to ram space

Code space full, fixing up btagsvc.dll to ram space

Code space full, fixing up BthAGPhonebook.dll to ram space

Code space full, fixing up celltsp.dll to ram space

Code space full, fixing up cplphone.dll to ram space

Code space full, fixing up phone.dll to ram space

Code space full, fixing up ril.dll to ram space

 

Taking the modules code offset from Figure 3, and subtracting it from the code offset of the previously listed module will determine the slot 1 space that can be recouped. (The data segment is need regardless of what we do and will remain the same.) So for msim.dll the code size is 0x02106000 – 0x020E4000, which is 136KB. The rollover order will be the same for every run of makeimg, the only exception is when slot 1 is low on space, it will pack in modules small enough to fit into remaining space. This is good news because one less module in slot 0 saves an additional 64KB. The consistent rollover order enables us to predict which files will rollover and in what order, this is illustrated in Table 1. The last two columns of this table illustrate the total space saved in slot 1 and total space used by these modules as they rollover. Notice how sms.dll consumes 24K of space in slot 1 and will consume 64K in slot 0.

 

Table 1

 

Rollover Order

Name

Space used

in slot 1 (KB)*

Space used

in slot 0 (KB)

 if rolled over*

Incremental space freed in slot 1 (KB)

Incremental space used in slot 0 (KB)

1st

sms.dll

24

64

24

64

2nd

simsec.dll

48

64

72

128

3rd

cspvoice.dll

64

64

136

192

4th

ccoreutl.dll

60

64

196

256

5th

ccordrv.dll

132

192

328

448

6th

netcfd3dm2_0.dll

156

192

484

640

7th

netcfag12_0.dll

236

256

720

896

8th

mscoree.dll

64

64

784

960

9th

msim.dll