Sunday, July 17, 2011

Using Ads to get free DropBox storage space.

(I did not "invent" this idea, someone told me about it. I figure you'll be interested in the experience, it was rather surprising for me.)

Google makes money on ads, oodles and oodles of it. Up until today, I never "understood" the value of ads.  Today, I got a chance to use ads, here is my story:

Dropbox is a storage service, if your friend signs up by clicking your referral URL, both you and your friend get 250MB of free storage up to 8GB of storage. This means if you can get 32 (8GB/250MB) friends to sign up you get lots of free storage.

As you can imagine, I don't have 32 friends, so the question is how do I get people to click on my URL? Ads!  I signed up for a Google AdWords account, and created an add which read - "Download DropBox and get free 250MB of storage". Here are the stats from my experiment:

  • Total Cost: $15.00
  • Cost/GB:$1.87/GB
  • Impressions (number of times add shown): 3.6K
  • Clicks: 257
  • Average Cost per Click 0.06
  • Conversions (times people installed DropBox after clicking) : 32

In this experiment each of impressions -> clicks -> conversions, is a 10x decrease, which seems astronomically high to me.  Seriously, 1 in a 100 people who saw my ad signed up for DropBox.  No wonder eyeballs are so valuable!

A final note for the clever among you, would it have been cheaper to purchase storage from dropbox instead of paying for ads?  DropBox pricing is $2.40/GB/Year,  and worse you can only purchase it in increments of 50GB.  Thus, using ads and the referral system is a good deal.

Saturday, July 2, 2011

Using WinDBG/CDB and SOS to find a thread given a ManagedThreadId

CDB and SOS have more power than most people realize, I’ll use this post to show some some handy cdb/sos tricks while at the same answering the simple question of which thread a ManagedThreadId maps to.

At the end of this post you’ll be able to:

  • Map a ManagedThreadId to the correct thread ordinal
  • Use CDB to convert from decimal to hex
  • Pipe CDB commands through pipeline commands like findstr

Full source for the simple app below can be found here. In the below app we want to figure which thread we’re running on so we can inspect the stack.

    internal class Program
{
private static void SleepForever() { Thread.Sleep(Int32.MaxValue); }

static void Main(string[] args)
{
const int countThreadsToStart = 20;
foreach (var unused in Enumerable.Range(0, countThreadsToStart))
{
// Instantiate thread that prints and stores its ManagedThreadId.
new Thread(() =>
{
// store ThreadId as an object as this forces boxing and lets us find the object on the stack via !dso in SOS.
object boxedThreadId = Thread.CurrentThread.ManagedThreadId;
Console.WriteLine("Find my threadId of {0}", boxedThreadId);
SleepForever();
}).Start();
}
SleepForever();
}
}


Step 1, run this app in CDB:



 PS C:\hgs\ig2600_blog\FindTheThread\bin> C:\bin_drop\public_dbg_64\cdb .\Debug\FindTheThread.exe

Microsoft (R) Windows Debugger Version 6.11.0001.404 AMD64
Copyright (c) Microsoft Corporation. All rights reserved.

CommandLine: .\Debug\FindTheThread.exe

Find my threadId of 3
Find my threadId of 4
Find my threadId of 5
Find my threadId of 6
Find my threadId of 7
Find my threadId of 8
Find my threadId of 9
Find my threadId of 10
Find my threadId of 11
Find my threadId of 12
Find my threadId of 13
Find my threadId of 14
Find my threadId of 15
Find my threadId of 16
Find my threadId of 17
Find my threadId of 18
Find my threadId of 19
Find my threadId of 20
Find my threadId of 21
Find my threadId of 22
(e28.c94): Break instruction exception - code 80000003 (first chance)
ntdll!DbgBreakPoint:
00000000`7744ef90 cc int 3
0:025> .loadby sos clr


Lets say our goal is to debug what’s running on thread 21, how do we know which thread ordinal we should debug on?   Luckily we have a !Threads command:



 0:025> !threads
ThreadCount: 22
UnstartedThread: 0
BackgroundThread: 1
PendingThread: 0
DeadThread: 0
Hosted Runtime: no
PreEmptive Lock
ID OSID ThreadOBJ State GC GC Alloc Context Domain Count APT Excepti
on
0 1 224c 000000000054dd80 200a020 Enabled 0000000002ac90b0:0000000002ac9fd0 00000000004ee680 0 MTA 2 2 c8c 0000000000553bd0 b220 Enabled 0000000000000000:0000000000000000 00000000004ee680 0 MTA (Finalizer)
4 3 1670 0000000000591620 200b020 Enabled 0000000002ac3be0:0000000002ac3fd0 00000000004ee680 0 MTA
5 4 1c84 0000000000578370 200b020 Enabled 0000000002ac4160:0000000002ac5fd0 00000000004ee680 0 MTA
6 5 1a54 000000000057a190 200b020 Enabled 0000000002ac6160:0000000002ac7fd0 00000000004ee680 0 MTA
7 6 2364 0000000000591f90 200b020 Enabled 0000000002aca160:0000000002acbfd0 00000000004ee680 0 MTA
8 7 b38 0000000000593570 200b020 Enabled 0000000002acc160:0000000002acdfd0 00000000004ee680 0 MTA
9 8 1c88 0000000000594df0 200b020 Enabled 0000000002ace160:0000000002acffd0 00000000004ee680 0 MTA
10 9 ef8 00000000005966b0 200b020 Enabled 0000000002ad0160:0000000002ad1fd0 00000000004ee680 0 MTA
11 a 21d0 0000000000597f70 200b020 Enabled 0000000002ad2160:0000000002ad3fd0 00000000004ee680 0 MTA
12 b 22a8 0000000000599830 200b020 Enabled 0000000002ad4160:0000000002ad5fd0 00000000004ee680 0 MTA
13 c 600 000000000059b0f0 200b020 Enabled 0000000002ad6160:0000000002ad7fd0 00000000004ee680 0 MTA
14 d 2388 000000000059c9b0 200b020 Enabled 0000000002ad8160:0000000002ad9fd0 00000000004ee680 0 MTA
15 e 19b8 000000000059e270 200b020 Enabled 0000000002ada160:0000000002adbfd0 00000000004ee680 0 MTA
16 f 1e9c 000000000059fb30 200b020 Enabled 0000000002adc160:0000000002addfd0 00000000004ee680 0 MTA
17 10 1c9c 00000000005a13f0 200b020 Enabled 0000000002ade160:0000000002adffd0 00000000004ee680 0 MTA
18 11 20c4 00000000005a2d40 200b020 Enabled 0000000002ae0160:0000000002ae1fd0 00000000004ee680 0 MTA
19 12 14bc 00000000005a4600 200b020 Enabled 0000000002ae2160:0000000002ae3fd0 00000000004ee680 0 MTA
20 13 1994 00000000005a5ec0 200b020 Enabled 0000000002ae4160:0000000002ae5fd0 00000000004ee680 0 MTA
21 14 19bc 00000000005a7780 200b020 Enabled 0000000002ae6160:0000000002ae7fd0 00000000004ee680 0 MTA
22 15 1ac 00000000005a9040 200b020 Enabled 0000000002ae8160:0000000002ae9fd0 00000000004ee680 0 MTA
23 16 2084 00000000005aa900 200b020 Enabled 0000000002aea160:0000000002aebfd0 00000000004ee680 0 MTA
0:025>


In this output, the first column is the thread ordinal (which is what the ~ operator works on).  The second column, is the OSID which is the ManagedThreadID,  but is inconveniently the value is displayed in hexdecimal.  To continue we need to find the thread ordinal, given an OSID of 21 in base 16. To convert 21 to hex, use the evaluation operator (?), and pass in an explicitly base 10 number by pre-pending 0n:



 0:025> ? 0n21
Evaluate expression: 21 = 00000000`00000015
0:025>


Now, re-run !threads, but pipe the output through findstr 15 . Findstr can be run via the .shell command:



 0:025> .shell -ci "!Threads" findstr 15"
15 e 19b8 000000000059e270 200b020 Enabled 0000000002ada160:0000000002adbfd0 00000000004ee680 0 MTA
22 15 1ac 00000000005a9040 200b020 Enabled 0000000002ae8160:0000000002ae9fd0 00000000004ee680 0 MTA


OSID 15, is thread 22, lets switch to it with the ~s operator:



0:025> ~22s
ntdll!NtDelayExecution+0xa:
00000000`7745009a c3 ret


To verify we have the correct thread, we can dump the stack and inspect the boxedThreadId Object. We do this with !dso to (dump stack objects) and then run !do (dump object) on the int32.



0:022> !dso
OS Thread Id: 0x1ac (22)
RSP/REG Object Name
0000000021F7E4C0 0000000002ac2110 Microsoft.Win32.SafeHandles.SafeFileHandle
0000000021F7E590 0000000002ac3048 System.IO.StreamWriter
0000000021F7E5C0 0000000002ac3318 System.Byte[]
0000000021F7E5E0 0000000002ac8f60 System.Threading.ExecutionContext
0000000021F7E638 0000000002ae7fe8 System.Int32
0000000021F7E640 0000000002ac2070 System.String Find my threadId of {0}
0000000021F7E680 0000000002ac1fe8 System.Threading.ContextCallback
0000000021F7E698 0000000002ac8f60 System.Threading.ExecutionContext
0000000021F7E6A0 0000000002ac8ef8 System.Threading.ThreadHelper
0000000021F7E6B0 0000000002ac8f60 System.Threading.ExecutionContext
0000000021F7E6C0 0000000002ac3490 System.IO.TextWriter+SyncTextWriter
0000000021F7E6D0 0000000002ac8f60 System.Threading.ExecutionContext
0000000021F7E6D8 0000000002ac8ef8 System.Threading.ThreadHelper
0000000021F7E6E0 0000000002ae7fe8 System.Int32
0000000021F7E6F0 0000000002ac8ea0 System.Threading.Thread
0000000021F7E700 0000000002ac8f60 System.Threading.ExecutionContext
0000000021F7E748 0000000002ac1fe8 System.Threading.ContextCallback
0000000021F7E750 0000000002ac8f60 System.Threading.ExecutionContext
0000000021F7E760 0000000002ac8ef8 System.Threading.ThreadHelper
0000000021F7E770 0000000002ac8ef8 System.Threading.ThreadHelper
0000000021F7E7A0 0000000002ac8f60 System.Threading.ExecutionContext
0000000021F7E7B0 0000000002ac8ef8 System.Threading.ThreadHelper
0000000021F7E7C0 0000000002ac8ef8 System.Threading.ThreadHelper
0000000021F7E810 0000000002ac8f20 System.Threading.ThreadStart
0000000021F7E978 0000000002ac8f20 System.Threading.ThreadStart
0000000021F7EBB0 0000000002ac8f20 System.Threading.ThreadStart
0000000021F7EBC8 0000000002ac8f20 System.Threading.ThreadStart

0:022>!do 0000000002ae7fe8
Name: System.Int32
MethodTable: 000007fee50bc848
EEClass: 000007fee4c40890
Size: 24(0x18) bytes
File: C:\Windows\Microsoft.Net\assembly\GAC_64\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
Fields:
MT Field Offset Type VT Attr Value Name
000007fee50bc848 400047b 8 System.Int32 1 instance 21 m_value
0:022>


Note the value is 21, exactly what we were looking for! Leave a comment if you have things you’d like to see me debug.

Sunday, June 26, 2011

Use Paint.Net to Highlight key areas in an image to get your point across

Earlier, I mentioned how important communication is to software engineering. A key tool in communication is being able to highlight portions of images.  For this task I use some basic features of Paint.Net.  In the below image I want to draw your attention to the name of the dress, and the name of the color of the dress. Without these highlighted regions, it's likely your eye wouldn't have picked up the points I wanted you to see.useForPaintNet

To perform these highlights in Paint.Net do the following:

  • Copy image into Paint.Net
  • Select the ellipse tool
  • Specify Fill Options as Draw shape with outline
  • Set Secondary color(the fill color) to yellow
  • Set transparency to 50 %
  • Draw ellipse

Saturday, June 25, 2011

Cool Tools: VirtuaWin

Virtual desktop software allow you have several desktops, and switch between them easily.  This is the equivalent of virtual memory, but for desktop real estate. Good virtual desktop software allows you to move windows between virtual desktops, and will have good usability including hot keys.  VirtuaWin is a great free virtual desktop app for windows,  go install it .  Once installed you should do the following:

  •     Customize the notification area to always show the VirtuaWin Tray Icon

  •     Use C-A-Right, C-A-Left, C-A-Up, C-A-Down to navigate between your desktops

  •     Left Click on the VirtuaWin icon to interact with your windows

  •     Right Click on the VirtuaWin icon to play with settings



Enjoy, and be sure leave comments if there are tools you'd like more information on.

Saturday, June 18, 2011

Cool Tools: Paint.Net

Paint.Net is a nice compromise between advanced picture editing software, and picture editing software usable by humans. The biggest problem I had with paint.net is figuring out how to download it. In what I assume is an attempt to make advertising dollars, this website exists that make it impossible to download Paint.net without accidentally downloading crap.  Here's a direct paint.net download link.



Update here are my usages of Paint.Net:

Tuesday, May 10, 2011

Cool IEnumerable Tricks Part 1: GroupBy

IEnumerable, or what most people refer to as LINQ, has changed the way I program. If you love LINQ and you already fully understand this blog post, leave a comment with a topic you'd like me to post about. If you're not so comfortable with LINQ, try to follow along. This post will only take a few minutes, but may change the way you program for life.

While we go through this blog post (and future posts in this series) make sure you understand how every function works, if you don't understand a function, you certainly won't understand the next function. Some of this stuff is hard to grasp and I find the best way to understand the code is to try it in a debugger. It took me a few tries to wrap my head around what is going on, so expect to spend some time experimenting with this code.

I learn best when I'm trying to solve a problem, so lets define a simple problem: How to determine if the random number generator equally distributes integers mod 10.

Step 1: Generate a stream of random numbers:

var seed = new Random();
var countSamples = 10*1000
var randomStream = Enumerable.Range(0,countSamples).Select(r=>seed.Next());


In the above sample, we generate countSamples random numbers. We do this by producing countSamples integers via Enumerables.Range, than for each of those integers we apply a transformation. In this case the transformation ignores the input value, and returns a random number.



Step 2: Compute how many of each random number have each modulus (0..9).



randomStream.GroupBy(r=>r%10).Select(x=>new {x.Key,count=x.Count()}).OrderBy(r=>r.Key);

In the above sample, we use GroupBy to split the random numbers into groups based on their %10 value. For each group, we return a new object containing the modulus (x.Key) and the count of each group. Finally we sort based on the modulus.  The output is below:



































































Key count

0



959



1



938



2



1037



3



1028



4



951



5



1009



6



1029



7



1011



8



1038



9



1000




In a future post I'll describe how to create our own IEnumerable functions.

Monday, April 18, 2011

Leading by praise: How to use a carrot when you really want a stick.

Many naive leaders believe they have two ways to enact change in their followers.  The first way is "the carrot" aka praising good behavior. The second way is "the stick"  aka criticizing bad behavior.  In my experience, the stick is extremely ineffective. I'd say the only time the stick has any positive impact is when the follower doesn't realize she's doing something wrong, or how serious that wrong thing is.  

The goal of this post is how to use a carrot when you want a stick.  First lets recall how to use a carrot effectively in general: Keep it timely, short, specific and often (the one minute manager is my hero).  Any time you see a new good behavior praise it.  When you praise,  be as specific as possible so the follower knows both the behavior you are praising and the valuable impact of that behavior.  It's easy to praise when things go well, but how do we praise when things are going poorly?

When things are going poorly, there's only one thing you can do - put your followers in a position where they perform good behaviors.  Scope their work so they have no choice but to perform good behaviors.  From that good behavior, apply praise.  Even if that behavior is accidental, still give praise.   Slowly, sometimes painfully slowly, you'll get more opportunities to give praise, and you'll get more good behaviors.  Keep giving praise, eventually the fruit of your labor will pay off.

Praising weak performers can be a time consuming delicate process, but it's like lighting a fire: at first you have to work really hard to spark the tinder, then pay close attention while the kindling catches to make sure you don't lose your delicate flame, finally you'll have a fire raging, and you can lean back and relax knowing not only have you lit a fire, but that fire can be used to light other fires.