I am doing iOS game development using the cocos2d framework, and I tremendously envy the ability for Eclipse Java programmers to hot swap their code while they are debugging their application (i.e. change out variable values within a method and have the program live update as if you had a REPL without having to recompile).
This seems like something that would be absolutely tremendously helpful when it came to iOS development, where my development environment is (obviously) Xcode and the language I am programming in is Objective C. I have googled around but havent been able to find anything - so I thought I would ask the community.
Does anyone know if there a way to Hot Swap code in Xcode while programming in Objective C?
Thanks in advance
There is a great plugin which allow changing code in live, working APP. It is called InjectionPlugin.
As FAQ says:
How does it work? The Objective-C runtime allows you to load a new version of a class into an application using a bundle even if there is already an implementation linked into the application. Swizzling is used as the bundle is loaded to switch the existing class to use the methods of the new implementation. This can be done repeatedly and works for OSX and iOS applications and on iOS devices.
I made some small video which shows how to install and use this plugin
http://nomtek.com/developers/how-to-install-and-use-injection-plugin-for-xcode/
Hope it helps!
Not possible with the current tools.
Keep in mind that iOS applications are signed -- if you change a single byte you'd have resign the whole thing. One could imagine making it work using runtime's support for dynamically adding and removing methods. But that'd surely require adding some extra stuff to support it on the device, and that's something that malware could easily take advantage of. So it's probably not a feature you'll be likely to see anytime soon.
By the way, Xcode versions 1.x-3.x did have a "Fix and Continue" feature. You could edit as you were debugging, use the Fix and Continue command, and continue running the updated code. I believe it was removed at some point, perhaps due to some combination of: requiring that your project be configured to use "zero link" and perhaps some other things; being less than completely reliable; probably not supporting iOS; the switch to llvm; other changes in Xcode 4. Maybe it'll come back someday -- if you want it back, file a bug -- but again, I think supporting it on iOS would be a challenge.
If you're just talking about changing variable values then you can achieve that surreptitiously via lldb (or, presumably) gdb. Supposing you had:
- (void)uselessMethod
{
NSString *localString = #"I'm some local text";
NSLog(#"%#", localString);
}
And put a breakpoint on the NSLog, at that point you could ask lldb to evaluate a reassignment of localString as a means of performing it. E.g.
po localString = #"Hat"
If you then allow program execution to continue, you should find that the reassignment has stuck. Similarly you can call any method or perform any other sort of assignment.
I've just tested this against Xcode 4.3.2.
You can hot swap a variable value in Xcode by:
expression <variable> = <value>;.
By having a break point in the place where you wanna change the value and doing the command in Xcode console.
Example:
// Messages From Console
(lldb) expression graphFlag = #"X"; // Update variable value
(__NSCFConstantString *) $0 = 0x36f95718 #"X" // Xcode prints the updated value
(lldb) expression graphFlag; // Printing value through expression command
(__NSCFConstantString *) $1 = 0x36f95718 #"X" // Hot Swapped variable value
Related
This is the umpteenth time I've tried to create an AUv3 plugin, all with various amounts of success (no complete joy, ever :/ )
It works in the app, but auval fails with
Bad Max Frames - Render should fail
ERROR: AudioUnitRender should have returned an error for too many frames but returned noErr
Current OS: 10.13.5
XCode: 9.4
created an AUv3 MacOS Instrument by creating a Cocoa Objective C app
added an AudioUnit app extension
added a Cocoa framework
etc. Details supplied if needed. (I made notes about my steps since I've never completely succeeded previously.) It is essentially the same as the Demo MacOS AUv3 instrument but I started the project from scratch. The only swift code is in SimplePlayEngine.swift. The rest is Objective C, Objective C++, and straight-up C++.
The only web search hit I found on this problem is add arg to prepareInputBufferList but there are two problems with this link:
Applying the change doesn't fix my problem
The demo AUv3 instrument builds and passes auval validation on my system with no modification.
I don't know if this is related but I find that NO breakpoint I set in the audiounit or related files (BufferedAudioBus.hpp) is ever hit. Since the audiounit functions in the app I must assume that the code is getting executed but something about the way it was built is wrong. (NSLog messages don't get printed either ... ??? (yes I know that when realtime rendering, printing to the console is a bad idea)).
I diffed between my AU source and the demo AU and the only changes are name changes. The "Build Phases" are comparable.
I know this is a pretty non-specific question, but I've run out of ideas. Does anyone have any ideas where to look to resolve the "Bad Max Frames" auval failure?
I'm completely stumped. I've been debugging for over a year and have only had this problem when the Build Configuration was set to Release. I have the Build Configuration set to Debug and I have checked to be sure I am attaching to the correct process and yet I still cannot see the values while stepping through the code. Has anybody else ran into this issue?
Here is a screen shot:
The value is returning, but I am unable to see the values of ANYTHING in this method or any of the other methods and I cannot figure out why.
Thank you for any hints you can give me.
============================== UPDATE ==================================
I've tried to print out the valued and this is the output I receive:
Notice though, that the value in the Variables view is correct for the result, even though I can't print it out. But the other values, like filePath should not be nil.
This is so weird.
============================== UPDATE ==================================
I put the breakpoint on the return statement and still no luck:
This time I see no value for result:
I was facing the same issue, On mouse hover and watch section for every variable it was showing nil value but while NSLog for those variables it was printing correct value.
now it's fixed. Try this
Go to Product menu > Scheme > Edit scheme or ⌘+<
In Run go to Info tab, change the build configuration to "debug".
Hope it will work.
#Lucy, ideally you shouldn't have to run in Release profile when debugging, Release is meant as an App Distribution profile with a small package size and little / no debug symbols.
But if you must debug on Release (e.g. due to Environment, data reasons), make sure you have the Optimization Level set to 'None' for Release.
You can reach the config above through:
1) Project wide settings - affects all Targets
2) Target specific setting - affects a single Target only
Don't forget to switch this back before App Store distribution. Lower Optimization will generate a larger ipk increasing your users' download time - also the potential dSYM generated.
Background
For context, Debug, Adhoc or Release profiles are purely to id different build configurations that XCode ships with. XCode starts projects with a Debug profile with no Optimization (and various other predefined config related to development & debugging), so symbol loading by the interpreter is possible - you're free to tweak this however you wish.
Theoretically able to establish a Release build profile that's identical to Debug.
I do see part of your problem. notice how self is nil?
That means you are in a method call for a de-allocated object. Turn on NSZombies to debug.
see this SO answer for how to enable NSZombie
Given that it is legal to send messages to nil in objective-C, I would suspect that the object is being deallocated as a side effect of calling doesLicenseFileExist, or by another thread.
You may find it helpful to put a breakpoint in IDLicenseCommands -dealloc method.
I had this problem, and turned "Link-Time Optimization" from "Incremental" to "No" and it resolved the debugging issue.
In my application i have worked for some part of the next version implementations,
So we need to prevent those some implementations for this version.
our superiors asked me to do with pre macro processor, having #ifDef, endif like that and you need to define the version number in buildsettings as preprocessor macro
I have added a user defined setting 'App_Version' in build Setting,
"
How do i use it like
#ifDef AppVersion 1.0
NSLOG (current version implementation)
else
NSLOG (NEXT version implementation)
Actually i was not much awair on it, so that my interpretation was poor
#if App_Version == 1.0
I don't remember exactly how new preprocessor macros are defined in XCode but I don't think that you are defining it correctly. See How do I define preprocessor macros in Xcode 4?
However, using preprocessor macros for such things is far from recommended.
You should
Create a method that will read the application version from the bundle
Call the method to check version and use standard if-elseif-else
With iPhone development I can't really imagine why would you use such a macro/method though. How many versions of an app do you want to build? All the applications I have implemented for iOS needed only 1 version - the latest one. I don't see any reason why would you like to build an older application version from current code.
Use a versioning system and if you are implementing features for the next version into current code, use a dedicated branch! Otherwise it's just a mess and your supervisor is ... not smart ... for not seeing it.
The compiler doesn't need (or want) to know anything about application version (it is information for the application submission process). This should not be done with preprocessor macros, unless you want to define your own (even then, I wouldn't recommend it). You should check the application version at runtime
[[NSBundle mainBundle] objectForInfoDictionaryKey:#"CFBundleVersion"];
and prevent new features from being accessible in the current version based on that. More generally I would consider using branching models like git flow to handle things like that. What will happen when you have 3,4,5 ... versions to handle. The preprocessor macros will become a nightmare to manage.
Context
As a university project we want to change the the pharo vm to use an object-table and see what happens.
We use a pharo-vm clone from github and VMMaker. Building the VM works fine.
To get started we added a primitive that returns an incremented Integer:
InterpreterPrimitives>>primitiveIntegerIncrement
"increments an integer"
self pushInteger: self popInteger + 1 .
and modified StackInterpreter class>>initializePrimitiveTable accordingly
MaxPrimitiveIndex := 576.
"... and so on ..."
(575 primitiveFail)
(576 primitiveIntegerIncrement))
And it works.
Problem
When we make changes to the VM we want to test-run already in the SmalltalkImage so we do not need to compile and see it did not work.
Something like:
StackInterpreter test: '1 inc'
And then I can debug if the primitive is wrong or an error occurs. Of course there needs to be done much more but how can I achieve this?
What we tried
category VMMaker-InterpreterSimulation class StackInterpreterSimulator. Trying the code in the comments
DoIt
^ (StackInterpreterSimulator new openOn: Smalltalk imageName) test
errors:
displayForm := 'Display has not yet been installed' asDisplayText form.
the ByteString does not understand asDisplayText
(CogVMSimulator new openOn: Smalltalk imageName) test
(InterpreterSimulator new openOn: Smalltalk imageName) test
error:
PrimitiveFailed: primitive #basicNew: in Array class failed
I also found this screen cast but it only debugs the VM from outside using gbd: http://vimeo.com/22485382#
Our project is hosted here: http://smalltalkhub.com/#!/~kirstin/PharoObjectTable
Current Status
We started implementing an object table. The lookup of attributes can go throught the object table. Full object table support and no usage of direct pointes is very tricky since pointers are expected everywhere. So we use pointers into the object table to identify when a lookup should go through the OT. We also found all object creation primitives and add new objects to the table.
How long is your project and how many people are you ? To me what you try to do is quite some work. Do you have good knowledge about low level behavior ?
To answer your question, the main problem here is that the cog simulator is not maintained in the pharo vm fork. This is because no one in the pharo crew use the simulator. We only use external debugging from gdb. In fact the pharo folks work mostly on VM plugins, the core of the VM is mainly maintained and developed by Eliot Miranda which works on Squeak. Therefore we report to him when there's a bug in the VM core.
For your project you would have to split it in at least 2 steps:
step 1: make the object table work with the stack VM
step 2: make the JIT work with your object table
Note that for step 2 I would recommend not to change the way an object access its header, therefore having a VW-like object table where you have the fixed size header on the one in the the object table, and the fields of the objects (and maybe header extensions) in the heap.
So use the StackVMSimulator and build the StackVM first. When everything will work (including context), you can think about hacking the JIT. Recently Guillermo Polito ported the Stack VM to the build process (see PharoSVMBuilder instead of PharoVMBuilder), a guy reported problems with this builder but you could hack it a bit to make it work.
Now to make the simulator work on Pharo 2.0 (which is the Pharo version of the generator image you have), you have to open the monticello browser and merge from Eliot's branch the Cog package (repo MCHttpRepository location: 'http: //source. squeak. org/VMMaker'), but not the latest Cog, the one at around the same date as the current VMMaker package of pharo-vm because the latest Cog and VMMaker of Eliot's branch are not stable.
The alternative being to start from Eliot's build image and merge things from the pharo branch. Here are infos about how to build the squeak development image (http://www.mirandabanda.org/cogblog/build-image/).
Then Eliot gave me this script once:
| cos |
cos := CogVMSimulator newWithOptions: #(Cogit SistaStackToRegisterMappingCogit).
cos desiredNumStackPages: 8.
cos openOn: 'my/favourite.image'.
cos openAsMorph; toggleTranscript; halt; run
You don't need the SistaStackToRegisterMappingCogit option. I guess some similar script with the StackVMSimulator should work.
Lastly there are some documentation about the simulator but it is only for the CogSimulator (these documentations expects you already knows how the StackSimulator works, and just give you hints about how to use it with the JIT):
http://www.mirandabanda.org/cogblog/2008/12/12/simulate-out-of-the-bochs/
and in one of the video named "Cog VM (part x)", x being from 1 to 6, Eliot shows how he uses the simulator to disassemble x86, print the stack and inspect the heap.
Another tip, ask your questions on the pharo mailing list (pharo users or pharo dev), because here no one may notice your question (fortunately someone pointed me out your question this time).
And tell on the pharo mailing list if you managed to run the simulator in Pharo 2.0, some people (as me) are very interested in it. I was planning to do it at some point.
Good luck ! Nice project anyway.
The last time I tried to use the simulator is roughly a year ago, and I did not make it work.
However, there are a few patches, which I assume never got integrated that might be of help:
https://code.google.com/p/cog/issues/detail?id=106
https://code.google.com/p/cog/issues/detail?id=107
https://code.google.com/p/cog/issues/detail?id=108
Issue 107 includes a patch for your asDisplayText issue.
Here are my two lines of code:
NSString *frontFilePath = [[NSBundle mainBundle] pathForResource:[self.bookendFileNames objectAtIndex:self.randomIndex] ofType:#"caf"];
NSLog(#"frontFilePath = %#", frontFilePath );
I put a break point on the second line and when there, I try to print it:
(lldb) po frontFilePath
But I get the following error:
error: variable not available
I'm confused because if I step over the NSLog statement, the variable does indeed print to the console.
For what it's worth, I'm trying to debug the first line since sometimes it returns NULL, tho I can't, as of now, figure out why.
This is an artifact of debugging optimized code. When the compiler's optimization is enabled in your build settings, it moves variables between memory and registers as it decides is best. At the point where you're examining the variable in lldb, it may not exist in registers or memory at all -- even though it looks like it should still be available for display.
It's possible it is a shortcoming of the debug information output by the compiler. Sometimes the compiler will copy a variable into a register for its use and only list that register location in the debug information. Later the register is repurposed for other uses; value is still present on the stack but the compiler hasn't told the debugger that the value can be found there.
The only way to really tell whether it's insufficient debug info or if the value genuinely doesn't exist at that particular instruction is to examine the assembly code by hand. As soon as you turn on optimization with the compiler, the source code becomes a weak view into what's actually being executed in any particular order.
Instead of wandering too far into the wacky world of optimized code debugging, I strongly recommend turning off optimization (Optimization Level in your Build Settings) for your build and debugging it that way, if at all possible. If you do need to debug your app with optimization, make sure you're building with the latest Apple LLVM compiler supported by your Xcode -- there is always work being done to improve optimized code debugging and you want to avail yourself of the most up to date tools you can.
The "Address Sanitizer" in the diagnostics seems to make the values of variables unavailable, too.
Schemes > Run > Diagnostics > Address Sanitizer
In Swift, possibly starting with Xcode 9 and still an issue in Xcode 10, this could even come up when code optimization is turned off in the build settings. As #carlos_ms has pointed out here, a temporary solution is to define the variable as mutable, i.e.
Turn
let foo = Bar().string
into
var foo = Bar().string
in order to cause optimization to skip on this variable. Note that this might not work in all instances.
In this case, a good ol' debugPrint() might help you out.