Posted: July 27th, 2010 | Author: Timac | Filed under: MacOSX, Programming | Tags: 10.6, Clipboard, Copy as Plain Text, MacOS X, NSService, Paste and Match Style, Services | No Comments »
In this post I talk on how to write a Service for MacOS X 10.6. If you don’t know what is a Service, Apple describes it here http://www.apple.com/macosx/refinements/:
The Services menu in Mac OS X lets you use features of one application while working in another. In Snow Leopard, services are more simplified, streamlined, and helpful. The Services menu is contextual, so it shows just the services appropriate for the application you’re using or content you’re viewing rather than all available services. You can access services with a right click of your mouse or Control-click of your trackpad. You can configure the menu to show only the services you want, and you can even create your own services using Automator.
The technical documentation from Apple on how to write your own Service is available here: Services Implementation Guide. Writing a Service for MacOS X 10.6 is not complicated in itself. Some useful documentation (on how to correctly write the Info.plist of the service) is available but it is hard to find a working sample. So the aim at this post is to provide a sample Service with the source code. The Service is really simple and can do 2 tasks:
- implement a functionality similar to the ‘Paste and Match Style’ feature available in most applications under the Edit menu.
- implement a functionality to copy the selection from the front application as plain text in the Clipboard.
Download: You can download here the compiled Service. To install it, unzip it, then move the ‘Citrouille.service’ in your ~/Library/Services folder and log out. If you are interested by the source code, the full sources are available here.
‘Paste and Match Style’: A simple implementation of the ‘Paste and Match Style’ functionality. When you copy text (with whatever fonts, sizes and colours it had), you can select ‘Paste and Match Style’ from the Services menu in any application to paste the text with the current style. This feature doesn’t have advantages over the built-in ‘Paste and Match Style’ from many MacOSX applications.
Figure 1: ‘Paste and Match Style’ in the Services menu

The implementation for ‘Paste and Match Style’ is really simple:
- (void)doPasteAsPlainText:(NSPasteboard *)pboard userData:(NSString *)userData error:(NSString **)error
{
NSString *outString = @"";
BOOL hasError = NO;
if(!hasError)
{
// Test for strings on the pasteboard.
NSArray *classes = [NSArray arrayWithObject:[NSString class]];
NSDictionary *options = [NSDictionary dictionary];
if (![[NSPasteboard generalPasteboard] canReadObjectForClasses:classes options:options])
{
outString = @"";
hasError = YES;
*error = NSLocalizedString(@"Error: There is no string in the pasteboard.", @"outputing nothing");
}
}
if(!hasError)
{
outString = [[NSPasteboard generalPasteboard] stringForType:NSPasteboardTypeString];
if(outString == nil)
{
outString = @"";
hasError = YES;
*error = NSLocalizedString(@"Error: There is no valid string in the pasteboard.", @"outputing nothing");
}
}
// Write the string onto the pasteboard.
[pboard clearContents];
[pboard writeObjects:[NSArray arrayWithObject:outString]];
}
‘Copy as Plain Text’: you can select a text in any application and select ‘Copy as Plain Text’ from the Services menu in any application. This will copy the selected text as plain text (no style) in the clipboard.
Figure 2: ‘Copy as Plain Text’ in the Services menu

- (void)doCopyAsPlainText:(NSPasteboard *)pboard userData:(NSString *)userData error:(NSString **)error
{
// Test for strings on the pasteboard.
NSArray *classes = [NSArray arrayWithObject:[NSString class]];
NSDictionary *options = [NSDictionary dictionary];
if (![pboard canReadObjectForClasses:classes options:options])
{
*error = NSLocalizedString(@"Error: couldn't get text.", @"pboard couldn't give string.");
return;
}
NSString *pboardString = [pboard stringForType:NSPasteboardTypeString];
[[NSPasteboard generalPasteboard] clearContents];
[[NSPasteboard generalPasteboard] writeObjects:[NSArray arrayWithObject:pboardString]];
}
Posted: March 17th, 2010 | Author: Timac | Filed under: Cornichon, MacOSX | Tags: Activity Monitor, Cornichon, Instruments, MacOSX, monitor, performance, process, profiling, snow leopard | No Comments »
Cornichon is a powerful tool you can use to dynamically profile Mac OS X applications on the system and track the process’ performance over time.
Figure 1: Profiling Safari with Cornichon
The Cornichon application includes the ability to:
- Profile Mac OS X applications
- Profile iPhone applications running in the iPhone simulator
- Examine the behavior of one or more processes
- See the different collected data of a process in real time in a graph
- Export the data as TAB files that you can later easily import in Apple Numbers or Microsoft Excel
With the Cornichon application, you can inspect different aspects of a process’ behavior:
- CPU usage
- Resident memory size (RSIZE)
- Resident private address space size (RPRVT)
- Total memory size (VSIZE)
- Number of threads
- …
Why using Cornichon?
Apple provides several tools that allow you to profile Mac OS X applications: Activity Monitor, Instruments, and some command line tools like ‘top’ or ‘ps’.
But none of these tools provide a real-time graph to profile a specific application:
- Activity Monitor has no graph view which makes it difficult to see how the process’ performance evolves over time.
- The ‘Activity Monitor’ template of Instruments only monitors the system workload but not the workload of a specific application.
Cornichon System Requirements
- Mac OS X 10.5.8 or later.
- Universal application (for both Intel and PowerPC-based Macs).
FAQ
Posted: March 6th, 2010 | Author: Timac | Filed under: MacOSX, Programming | Tags: 10.6, 32-bit, 64-bit, K64, kernel, MacOS, MacOSX, snow leopard | 1 Comment »
It might be useful in some cases to know if the MacOS kernel is running in the 32-bit or 64-bit (K64) mode. This is useful for example if you write an application like ‘System Profiler’ that displays the details of the currently running system:
Obviously this post only applies to intel machines running on Snow Leopard (see my previous post ‘Intel 64-bit summary’). To simplify the code, I assume that you compiled your application with the 10.6 SDK.
In the IOKit framework on 10.6 the function OSKextGetRunningKernelArchitecture is defined as followed in the OSKext.h header:
/*!
* @function OSKextGetRunningKernelArchitecture
* @abstract Returns the architecture of the running kernel.
*
* @result
* The architecture of the running kernel.
* The caller does not own the returned pointer and should not free it.
*
* @discussion
* This function consults the kernel task and returns a pointer to the
* NXArchInfo struct representing its architecture.
* The running kernel's architecture does not necessarily match that
* of user space tasks (for example, a 32-bit user space task could be
* running with a 64-bit kernel, or vice-versa).
*/
CF_EXPORT const NXArchInfo *
OSKextGetRunningKernelArchitecture(void)
AVAILABLE_MAC_OS_X_VERSION_10_6_AND_LATER;
This function is the one I use to determine the current running kernel mode:
#import <Foundation/Foundation.h>
#import <mach-o/arch.h>
// OSKextGetRunningKernelArchitecture is not available in
// the 10.6 SDK headers, but exported since 10.6.
extern const NXArchInfo *OSKextGetRunningKernelArchitecture(void);
static BOOL Is64BitKernel()
{
BOOL isK64 = NO;
const NXArchInfo *archInfo = OSKextGetRunningKernelArchitecture();
if (archInfo != NULL)
{
isK64 = ((archInfo->cputype & CPU_ARCH_ABI64) != 0);
}
return isK64;
}
int main (int argc, const char * argv[])
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
fprintf(stderr, "Is 64-bit kernel: %d\n", Is64BitKernel());
[pool drain];
return 0;
}
And don’t forget to link against the IOKit framework.
Also note that it is quite easy to make the previous code compiling with the 10.5 (or earlier) SDK by dynamically getting the address of the OSKextGetRunningKernelArchitecture function and checking if the address is not NULL.
Posted: January 30th, 2010 | Author: Timac | Filed under: MacOSX, Programming | Tags: 32-bit, Finder, LaunchServices, LSArchitecturesForX86_64, MacOSX, Open in 32-bit mode, Open using Rosetta, rosetta, Universal Binary | No Comments »
As you know, it is possible to force an Universal Binary (ppc, i386, x86_64) to run in 32-bit mode (i386) even if the machine could run x86_64 binaries. And you can even force the application to run in Rosetta (ppc). To select in which mode the application should run, simply display the info of the application in the Finder and you should see 2 checkboxes:
- Open in 32-bit mode
- Open using Rosetta
As far as I know, the first checkbox should always be visible on an Intel machine as long as the application binary contains the x86_64 and i386 architecture.
The Rosetta checkbox will only be visible on an Intel machine if the application binary contains the ppc architecture. Note also that the developer can prevent the checkbox to appear in the Finder by setting the key ‘LSRequiresNativeExecution‘ to true in the ‘Info.plist’ file.
You may wonder where these flags are stored on the machine. You might have noticed that if you set one flag on an application, trash the application and the preferences, and reinstall it, the flag will still be set. So the flag is definitively not stored in the application package or in the application preferences.
In fact the flags are stored in the file ‘com.apple.LaunchServices.plist’ in the user Preferences folder (~/Library/Preferences/com.apple.LaunchServices.plist). That means that several users on the same machine could have different flags set for the same applications.
Before looking at this file, let’s imagine you have an application called ‘MyApp’ which was compiled for the ppc, i386 and x86_64 architectures. In the Finder you can duplicate this application 2 times. For one instance, check the ‘run in 32-bit mode’, force another instance to run in Rosetta, and leave the original instance unchanged (run as x86_64):
Here is what you will see if you open now the file ‘~/Library/Preferences/com.apple.LaunchServices.plist’ with Property List Editor:
It’s fairly easy to understand how the flags are stored. The file contains a dictionary ‘LSArchitecturesForX86_64′. The keys of this dictionary are the applicationIDs (‘org.timac.MyApp’ for example). For each applicationID, there is an array containing the information for the different instances of the application. The information of each instance is a pair (path of the application, architecture of the application):
- The first item in the array is an alias to the first instance of application.
- The second item is the architecture of the first instance of the application.
- The third item is an alias to the second instance of application.
- The fourth is the architecture of the second instance of the application.
- …
Following is the source code for a small application that will read the content of the file ‘com.apple.LaunchServices.plist’ and will print the paths of each application whose flags (‘Open in 32-bit mode’ or ‘Open using Rosetta’) were changed in the Finder. It will also display the architecture selected in the Finder for each of the application. This application will output something like:
applicationID: org.timac.MyApp
Path: /Applications/MyApp.app
will run as: x86_64
Path: /Applications/MyApp 32-bit.app
will run as: i386
Path: /Applications/MyApp Rosetta.app
will run as: ppc
The source code:
#import <Foundation/Foundation.h>
// This function takes as parameter the data of the aliases
// stored in the com.apple.LaunchServices.plist file.
// It returns the resolved path as string.
static NSString *getResolvedAliasPath(NSData* inData)
{
NSString *outPath = nil;
if(inData != nil)
{
const void *theDataPtr = [inData bytes];
NSUInteger theDataLength = [inData length];
if(theDataPtr != nil && theDataLength > 0)
{
// Create an AliasHandle from the NSData
AliasHandle theAliasHandle;
theAliasHandle = (AliasHandle)NewHandle(theDataLength);
bcopy(theDataPtr, *theAliasHandle, theDataLength);
FSRef theRef;
Boolean wChang;
OSStatus err = noErr;
err = FSResolveAlias(NULL, theAliasHandle, &theRef, &wChang);
if(err == noErr)
{
// The path was resolved.
char path[1024];
err = FSRefMakePath(&theRef, (UInt8*)path, sizeof(path));
if(err == noErr)
outPath = [NSString stringWithUTF8String:path];
}
else
{
// If we can't resolve the alias (file not found),
// we can still return the path.
CFStringRef tmpPath = NULL;
err = FSCopyAliasInfo(theAliasHandle, NULL, NULL,
&tmpPath, NULL, NULL);
if(err == noErr && tmpPath != NULL)
outPath = [(NSString*)tmpPath autorelease];
}
DisposeHandle((Handle)theAliasHandle);
}
}
return outPath;
}
// This function prints the architecture for each application
// in the com.apple.LaunchServices.plist file
static void dumpLSArchitecturesForX86_64()
{
// The path of the com.apple.LaunchServices.plist file.
NSString *prefsPath =
@"~/Library/Preferences/com.apple.LaunchServices.plist";
prefsPath = [prefsPath stringByExpandingTildeInPath];
NSDictionary *mainDict =
[NSDictionary dictionaryWithContentsOfFile:prefsPath];
if(mainDict != nil)
{
// We are only interested by the
// "LSArchitecturesForX86_64" dictionary.
NSDictionary *architectureDict =
[mainDict objectForKey:@"LSArchitecturesForX86_64"];
// Get the list of applications.
// The array is ordered by applicationID.
NSArray *applicationIDArray = [architectureDict allKeys];
if(applicationIDArray != nil)
{
// For each applicationID
NSUInteger i = 0;
for(i = 0 ; i < [applicationIDArray count] ; i++)
{
NSString *applicationID =
[applicationIDArray objectAtIndex:i];
NSArray *appArray =
[architectureDict objectForKey:applicationID];
NSLog(@"-------------------------");
NSLog(@"applicationID: %@", applicationID);
// For each instance of the application,
// there is a pair (Alias, architecture).
// The alias is stored as a NSData
// and the architecture as a NSString.
NSUInteger j = 0;
for(j = 0 ; j < [appArray count] / 2 ; j++)
{
NSLog(@"\n");
// Just for safety
if(j * 2 + 1 < [appArray count])
{
NSData *aliasData =
[appArray objectAtIndex:j * 2];
NSString *theArch =
[appArray objectAtIndex:j * 2 + 1];
if(aliasData != nil && theArch != nil)
{
// Get the path of the application
NSString *resolvedPath =
getResolvedAliasPath(aliasData);
NSLog(@"\t Path: %@", resolvedPath);
NSLog(@"\t will run as: %@", theArch);
}
}
}
}
}
}
}
int main (int argc, const char * argv[])
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
dumpLSArchitecturesForX86_64();
[pool drain];
return 0;
}
Posted: January 20th, 2010 | Author: Timac | Filed under: Programming | Tags: 64-bit PICT CGImageRef Xcode x86_64 | No Comments »
QuickDraw is the legacy 2D drawing engine for Macintosh computers and was deprecated in Tiger (10.4) but is still available for 32-bit applications on 10.4, 10.5 and 10.6. 64-bit applications can’t use QuickDraw yet and Apple recommends to switch to other technologies like Quartz 2D.
With the QuickDraw technology, you could create PICT images. The picture format was the graphics metafile format in Mac OS 9 and earlier. A picture contains a recorded sequence of QuickDraw imaging operations and associated data, suitable for later playback to a graphics device on any platform that supports the PICT format.
Apple made a good ‘Quartz Programming Guide for QuickDraw Developers’ guide that explains you how to convert your old PICT data to more modern images.
In this post I explain how to read PICT data in a 64-bit application. This might be useful if you ported your application to 64-bit but you want it to be able to read some old data generated by a previous version of your app (and convert the images to a more modern format).
Here is a link to download a small PICT file. If you try to open this image in Preview running in 64-bit on 10.6, you will be told that the PICT format is unsupported but you can run Preview in 32-bit compatibility mode:

There is yet a way to have PICT support in 64-bit apps: you can use the deprecated (but available on x86_64) class NSPICTImageRep. Using this class, you can get an NSImage out of the PICT data.
Here is the source code:
// Get the data contained in the PICT file
NSData *data = [NSData dataWithContentsOfFile:@"/tmp/sample.pict"];
// Returns an NSPICTImageRep object initialized with the specified data.
NSPICTImageRep *imageRep = [[NSPICTImageRep alloc] initWithData:data];
// Create an NSImage and add the specified image representation object
NSImage *image = [[NSImage alloc] init];
[image addRepresentation:imageRep];
// Do something with the NSImage
// Cleanup
[imageRep release];
When you use this code in a 64-bit app, you will notice that a 32-bit process called ‘picd’ (/usr/sbin/pictd) is started. This is this process that really does the job. As you can also see in Activity Monitor, the 64-bit version of QuickLook uses this ‘pictd’ process to generate the previews of PICT files.
So why does Preview not use this 32-bit process and instead display an error? Apple probably wants to get rid of this old PICT format…
Posted: January 8th, 2010 | Author: Timac | Filed under: MacOSX | Tags: 10.4, 10.5, 10.6, 64-bit, intel, kernel, leopard, MacOS, MacOSX, snow leopard, tiger | 6 Comments »
Since people are confused regarding Intel 64-bit, here is a brief summary of what can run on which Intel processor.
To run a 64-bit application, you obviously need a 64-bit processor. All the machines that Apple currently ships have a 64-bit processor.
- Only the ‘Intel Core Solo‘ and ‘Intel Core Duo‘ processors are 32-bit. On such machines, you won’t be able to run 64-bit apps.
- The ‘Intel Core 2 Duo‘ and ‘Xeon‘ processors are 64-bit.
Useful link on Apple Support: How to tell if your Intel-based Mac has a 32-bit or 64-bit processor.
If you have a 64-bit processor (‘Intel Core 2 Duo’ or better), there are 3 levels of 64-bit applications:
- to run a 64-bit faceless application: you need 10.4 and later.
- to run a 64-bit application with UI (Cocoa): you need 10.5 and later.
- to run the 64-bit kernel (and run 64-bit kernel extensions): you need 10.6 and later and a K64-capable machine (the EFI needs to be 64-bit). By default only the Xserve (2008 and later) will run the 64-bit kernel. On other K64-capable machines, you have to explicitely turn on the 64-bit kernel to use it.
Useful link on Apple Developer: Introduction to 64-Bit Transition Guide.
Posted: January 3rd, 2010 | Author: Timac | Filed under: Uncategorized | Tags: einstein, Einstein Puzzle, Einstein's Puzzle, énigme, énigme d'Einstein | 2 Comments »
This post is written in french and talks about a bad translation in the “Einstein’s Puzzle”.
L’énigme d’Einstein (aussi connue sous le nom de “l’énigme des 5 x 5″) est un jeu de logique. Son nom vient du fait qu’il aurait été inventé par Albert Einstein alors qu’il était enfant. Cependant ce jeu est aussi parfois attribué à Charles Lutwidge Dodgson.
Après cette brève introduction, voici l’énoncé en français que vous trouverez sur la plupart des sites internet français :
Il y a cinq maisons de 5 couleurs différentes.
Dans chaque maison vit une personne de nationalité différente.
Chacun des 5 propriétaires boit un certain type de boisson, fume un certain type de cigares et garde un certain animal domestique.
Hypothèses :
1. L’Anglais vit dans une maison rouge.
2. Le Suédois a des chiens comme animaux domestiques.
3. Le Danois boit du thé.
4. La maison verte est à gauche de la maison blanche.
5. Le propriétaire de la maison verte boit du café.
6. La personne qui fume des Pall Mall a des oiseaux.
7. Le propriétaire de la maison jaune fume des Dunhill.
8. La personne qui vit dans la maison du centre boit du lait.
9. Le Norvégien habite la première maison.
10. L’homme qui fume les Blend vit à côté de celui qui a des chats.
11. L’homme qui a un cheval est le voisin de celui qui fume des Dunhill.
12. Le propriétaire qui fume des Blue Master boit de la bière.
13. L’Allemand fume des Prince.
14. Le Norvégien vit juste à côté de la maison bleue.
15. L’homme qui fume des Blend a un voisin qui boit de l’eau.
Qui à le poisson ? Une seule réponse est possible…
La semaine dernière je ne connaissais pas cette énigme et j’ai essayé de la résoudre. Après quelques heures de reflexions, j’ai trouvé une solution à l’énigme. L’énoncé annonce qu’il y a une seule réponse possible. Cependant en regardant sur internet LA réponse officielle, elle est différente de la mienne !
Voila la réponse “officielle” (J’ai entouré en rouge la réponse de l’énigme et la condition 4) :

Et voici la solution que j’ai trouvée :

Vous pouvez vérifier, ma réponse répond bien aux 15 conditions. Pourtant l’énoncé assure qu’il existe une unique solution ! En comparant l’énoncé français et anglais de l’énigme, je me suis aperçu que j’ai interprété la condition 4 d’une manière plus générale que prévue.
La condition 4 dit : “La maison verte est à gauche de la maison blanche”. Avec ma solution la maison verte est bien à gauche de la maison blanche, mais pas juste à gauche : Il y a des maisons entre la maison verte et la maison blanche.
Contrairement à la condition 14 “Le Norvégien vit juste à côté de la maison bleue”, la condition 4 ne précise pas le direct voisinage. J’en ai donc déduit qu’il y a peut être des maisons entre la maison verte et la maison blanche.
L’énoncé anglais précise le direct voisinage et évite donc de trouver plusieurs solutions au problème. Il semble donc qu’il s’agisse d’une erreur de traduction.
Voici l’énoncé en anglais du “Einstein’s Puzzle” :
Let us assume that there are five houses of different colors next to each other on the same road. In each house lives a man of a different nationality. Every man has his favorite drink, his favorite brand of cigarettes, and keeps pets of a particular kind.
1. The Englishman lives in the red house.
2. The Swede keeps dogs.
3. The Dane drinks tea.
4. The green house is just to the left of the white one.
5. The owner of the green house drinks coffee.
6. The Pall Mall smoker keeps birds.
7. The owner of the yellow house smokes Dunhills.
8. The man in the center house drinks milk.
9. The Norwegian lives in the first house.
10. The Blend smoker has a neighbor who keeps cats.
11. The man who smokes Blue Masters drinks bier.
12. The man who keeps horses lives next to the Dunhill smoker.
13. The German smokes Prince.
14. The Norwegian lives next to the blue house.
15. The Blend smoker has a neighbor who drinks water.
The question to be answered is: Who keeps fish?
Comme vous l’avez vu, la version anglaise utilise le mot “just” dans la condition 4 : “The green house is just to the left of the white one”.
Sur Google, j’ai trouvé d’autres versions de l’énoncé en anglais qui mettent bien l’accent sur la mitoyenneté des 2 maisons :
“The green house is immediately to the right of the ivory house”.
“The green house is on the immediate left of the white house as you stare at the front of the 5 houses”.
Je propose donc de changer la condition 4 en :
“La maison verte est à juste gauche de la maison blanche”
Posted: September 27th, 2009 | Author: Timac | Filed under: Uncategorized | 1 Comment »
I am back from vacations and had a 11 hours flight with AirAustral. On board each seat has an embedded computer. With this computer, you can watch movies, play games, …
This machine is running on Linux which is downloaded and executed using Redboot as you can see in the following picture:

The hardware appears to be from Panasonic Avionics Corporation, probably a Panasonic X Series:

How did I made this picture?: I found a bug that caused the computer to deadlock. I tested it on 4 machines in 2 different aircrafts: 100% reproducible.
Steps to reproduce:
- Go in the Games section.
- Start the game ‘Who wants to be Millionaire?’.
- Play one game.
- Quit the game.
Result:
The machine will display a message “Please wait…” and a deadlock will occur.
You then need to ask a stewardess to reboot your machine. The reboot is quite slow and takes around 5 minutes.
PS: I don’t know where to send the bug report
Posted: September 6th, 2009 | Author: Timac | Filed under: Debugging, MacOSX, Programming | No Comments »
Here are different solutions to display automatically a backtrace when entering a specific function in your application. As an example we will take the following program. It’s a really simple program: the main function calls the function “function1″ which prints a string.
#include <stdio.h>
void function1()
{
fprintf(stderr, "Who called this function?\n");
}
int main()
{
function1();
return 0;
}
1- With GDB
The first solution is to set a breakpoint to the function “function1″ and run your program in the debugger. Each time GDB breaks you can inspect the backtrace and see why your function is called.
You can extend this method and automatically dump a backtrace each time the function is called by creating a command on a breakpoint in GDB. You can indeed give any breakpoint a series of commands to execute when your program stops due to that breakpoint.
Following is a simple GDB script that can be loaded using the source command in GDB:
# We create a breakpoint using "future break"
# If we break due to this breakpoint, the command
#following will be executed.
fb function1
command
backtrace
continue
end
For such a script it is faster to manually type the script in GDB with some abbreviations (“c” for continue, “bt” for backtrace):
(gdb) fb function1
Breakpoint 1 at 0x100000eac
(gdb) command
Type commands for when breakpoint 1 is hit, one per line.
End with a line saying just “end”.
>bt
>c
>end
(gdb) r
Starting program: a.out
Reading symbols for shared libraries +. done
Breakpoint 1, 0x0000000100000eac in function1 ()
#0 0x0000000100000eac in function1 ()
#1 0x0000000100000edc in main ()
Who called this function?
Program exited normally.
(gdb)
2- Without GDB
The solution with GDB is powerful and requires no code change but it is slow (you need to run in the debugger). It is possible to display the backtraces without GDB by using the backtrace() and backtrace_symbols() functions available in 10.5 and later. Here is a sample code to show how to use these functions:
#include <execinfo.h>
#include <stdio.h>
#define BACKTRACE_MAX_DEPTH 20
static void dumpBacktrace()
{
void *stack_frame[BACKTRACE_MAX_DEPTH];
// backtrace() writes the function return addresses of the current
// call stack to the array of pointers referenced by array.
// At most, size pointers are written.
// The number of pointers actually written to array is returned.
int number_of_frames = backtrace(stack_frame, BACKTRACE_MAX_DEPTH);
// backtrace_symbols() attempts to transform a call stack obtained
// by backtrace() into an array of human readable strings using
// dladdr(). The array of strings returned has size elements.
// It is allocated using malloc() and should be released using free().
// There is no need to free the individual strings in the array.
char** human_readable_frames =
backtrace_symbols(stack_frame, number_of_frames);
fprintf(stderr, "--------------------\nDumpBacktrace:\n");
int i = 0;
for (i = 0; i < number_of_frames; i++)
{
fprintf(stderr, "\t %s\n", human_readable_frames[i]);
}
// This needs to be released
free(human_readable_frames);
}
void function1()
{
fprintf(stderr, "Who called this function?\n");
dumpBacktrace();
}
int main()
{
function1();
return 0;
}
Here is the output:

Posted: August 6th, 2009 | Author: Timac | Filed under: Debugging, MacOSX, Programming | 2 Comments »
To determine the maximum number of files your application can open, there is a function getrlimit() available. Here is what tells us the man page:
int getrlimit(int resource, struct rlimit *rlp);
A resource limit is specified as a soft limit and a hard limit. When a soft limit is exceeded a process may receive a signal (for example, if the cpu time or file size is exceeded), but it will be allowed to continue execution until it reaches the hard limit (or modifies its resource limit). The rlimit structure is used to specify the hard and soft limits on a resource,
struct rlimit {
rlim_t rlim_cur; /* current (soft) limit */
rlim_t rlim_max; /* hard limit */
};
Let’s create an application to print the soft and hard limit. I simply created a new Cocoa project and add this awakeFromNib function:
#include <stdio.h>
#include <sys/resource.h>
-(void)awakeFromNib
{
struct rlimit lim;
int theErr = getrlimit(RLIMIT_NOFILE, &lim);
if(theErr == 0)
{
if(lim.rlim_max == RLIM_INFINITY)
fprintf(stderr, "soft: %llu ; hard: RLIM_INFINITYn", lim.rlim_cur);
else
fprintf(stderr, "soft: %llu ; hard: %llun", lim.rlim_cur, lim.rlim_max);
}
else
{
fprintf(stderr, "getrlimit returned an error %dn", theErr);
}
}
What would you expect? 256? 512? 1024? More?
Well the value depends on how you run the application! Here are the results on 10.5.8 i386:
- If you run the application by double clicking on the icon in the Finder:
soft: 256 ; hard: RLIM_INFINITY
- If you run the application from the terminal:
soft: 256 ; hard: RLIM_INFINITY
- If you run the application from Xcode without gdb:
soft: 2560 ; hard: RLIM_INFINITY
- If you run the application from Xcode with gdb:
soft: 10240 ; hard: RLIM_INFINITY
- If you run the application from the Terminal in gdb:
soft: 10240 ; hard: RLIM_INFINITY
- If you run a slightly modified application from the Finder and attach it to gdb and call manually the function:
soft: 256 ; hard: RLIM_INFINITY
This is quite confusing! I made a bug report: radar://7123983