annotation
The article describes the main steps to ensure the correct transfer of 32-bit Windows applications to 64-bit Windows systems. Although the article is intended for developers using C / C ++ in Visual Studio 2005/2008, it will be useful for other developers planning to port their applications to 64-bit systems.
Introduction
The article describes the main issues faced by developers who plan to migrate 32-bit programs to 64-bit systems. Of course, the list of issues considered is not complete, but I would like to hope that an extended version of this article will be offered over time. The author will be grateful for reviews, comments and questions that will improve the information content of this article.
1. Step one. 64 bits can be different.
Let's see
In the framework of computer architecture, the term "
64-bit " means 64-bit integers and other types of data having a size of 64 bits. “64-bit” systems can be understood as 64-bit microprocessor architectures (for example, EM64T, IA-64) or 64-bit operating systems (for example, Windows XP Professional x64 Edition) [
1 ].
AMD64 (aka x86-64, Intel 64, EM64T, x64) is a 64-bit microprocessor architecture and the corresponding instruction set developed by AMD [
2 ]. This instruction set was licensed by Intel under the name EM64T (Intel64). The AMD64 architecture is an extension of the x86 architecture with full backward compatibility. The architecture is widely used as a base of personal computers and workstations.
IA-64 is a 64-bit microprocessor architecture developed jointly by Intel and Hewlett Packard [
3 ]. Implemented in Itanium and Itanium 2 microprocessors [
4 ]. The architecture is mainly used in multiprocessor servers and cluster systems.
AMD64 and IA-64 are two different 64-bit architectures that are not compatible with each other. Therefore, developers should immediately decide whether to support both of these architectures or only one. In most cases, if you do not develop highly specialized software for cluster systems or do not implement your high-performance DBMS, then with a high probability you only need to implement support for the AMD64 architecture, which has become much more common than IA-64. This applies particularly to software for the personal computer market, which is almost 100 percent occupied by the AMD64 architecture.
Further in the article we will talk only about the AMD64 architecture (EM64T, x64), since its use is now the most relevant for application software developers.
Speaking of different architectures, we should mention the concept of "
data model ". The data model should be understood as the ratios of the types of dimensions adopted within the framework of the development environment. For a single operating system, there may be several development tools that follow different data models. But usually only one model prevails, the most appropriate hardware and software environment. An example is the 64-bit Windows operating system, in which the native data model is
LLP64 . But for compatibility, the 64-bit Windows system supports the execution of 32-bit programs that operate in the
ILP32LL data model mode. Table N1 provides information on the main data models.
Table N1. Data models')
The data model used leaves a mark on the process of developing 64-bit applications, since the code of the programs must take into account the width of the data used [
5 ].
2. Step Two. Find out if you need a 64-bit version of your product.
We should start mastering 64-bit systems with the question “Do we really need to rebuild our project for a 64-bit system?”. This question must be answered, but without haste, thinking. On the one hand, it is possible to lag behind its competitors, in time without offering 64-bit solutions. On the other hand, it is possible to waste time on a 64-bit application, which will not give any competitive advantages.
We list the main factors that will help you make a choice.
2.1. Application Life Cycle Time
You should not create a 64-bit version of the application with a short life cycle. Thanks to the
WOW64 subsystem, old 32-bit applications work quite well on 64-bit Windows systems and therefore making the program 64-bit, which will cease to be supported after 2 years, has no meaning [
6 ]. Moreover, practice has shown that the transition to 64-bit versions of Windows has dragged out and perhaps the majority of your users in the short term will use only the 32-bit version of your software solution.
If you plan for a long-term development and long-term support of the software, you should start working on the 64-bit version of your solution. This can be done slowly, but keep in mind that the longer you do not have a full 64-bit version, the more difficulties you may have with the support of such an application installed on 64-bit versions of Windows.
2.2. Application resource intensity
Recompiling a program for a 64-bit system will allow it to use huge amounts of RAM, and will also speed up its speed by 5-15%. A 5-10% increase will occur due to the use of the architectural capabilities of a 64-bit processor, for example, a larger number of registers. Another 1% -5% increase in speed is due to the absence of a WOW64 layer, which translates API calls between 32-bit applications and a 64-bit operating system.
If your program does not work with large amounts of data (more than 2GB) and its speed is not critical, then switching to a 64-bit system in the near future is not so relevant.
By the way, even simple 32-bit applications can benefit from their launch in a 64-bit environment. You probably know that a program compiled with the / LARGEADDRESSAWARE key: YES can allocate up to 3 gigabytes of memory if the 32-bit Windows operating system is running with the / 3gb key. The same 32-bit program running on a 64-bit system can allocate almost 4 GB of memory (in practice, about 3.5 GB).
2.3. Library Development
If you develop libraries, components or other elements with the help of which third-party developers create their software, then you should be quick to create a 64-bit version of your products. Otherwise, your clients interested in the release of 64-bit versions will be forced to look for alternative solutions. For example, some developers of software and hardware protection responded with a long delay to the emergence of 64-bit programs, which led a number of clients to look for other tools to protect their programs.
An additional advantage of the release of the 64-bit version of the library is that you can sell it as a separate product. Thus, your customers who wish to create both 32-bit and 64-bit applications will be forced to purchase 2 different licenses. For example, such a policy is used by Spatial Corporation when selling the Spatial ACIS library.
2.4. Dependence of your product on third-party libraries
Before planning work on creating 64-bit versions of your product, find out if there are 64-bit versions of libraries and components that are used in it. Also find out what the pricing policy is in relation to the 64-bit version of the library. All this can be found by visiting the website of the library developer. If there is no support, then look for alternative solutions that support 64-bit systems in advance.
2.5. 16-bit applications
If your solutions still have 16-bit modules, then it's time to get rid of them. The operation of 16-bit applications in 64-bit versions of Windows is not supported.
Here we should clarify one point related to the use of 16-bit installers. They are still used to install some 32-bit applications. A special mechanism has been created that replaces a number of the most popular 16-bit installers with newer versions on the fly. This may cause an incorrect opinion that 16-bit programs still work in a 64-bit environment. Remember, it is not.
2.6. The presence of a code in assembler
Do not forget that the use of a large amount of code in assembly language can significantly increase the cost of creating a 64-bit version of the application.
After weighing all the listed facts, all the pros and cons, decide whether you should transfer your project to 64-bit systems. And if so, let's go further.
3. Step Three. Tools
If you have decided to develop a 64-bit version of your product and are willing to spend time on it, this does not guarantee success. The fact is that you must have all the necessary tools and there may be unpleasant incidents.
The easiest, but also the most irresistible, can be the problem of the lack of a 64-bit compiler. The article is written in 2009, but still there is no 64-bit C ++ Builder compiler from Codegear [
7 ]. Its release is expected only by the end of this year. It is impossible to get around a similar problem, unless of course, but to rewrite the entire project, for example, using Visual Studio. But if everything is clear with the lack of a 64-bit compiler, other similar problems may turn out to be more secretive and get out at the stage of work on transferring the project to a new architecture. Therefore, I would like to advise in advance to study whether there are all the necessary components that are required to implement the 64-bit version of your product. You may face unpleasant surprises.
Of course, it is impossible to list everything that may be necessary for a project here, but still I will offer a list that will help you understand and perhaps recall other points that are necessary to implement your 64-bit project:
3.1. Presence of 64-bit compiler
It is difficult to say something about the importance of having a 64-bit compiler. He just has to be.
If you are planning to develop 64-bit applications using the latest version (at the time of writing) Visual Studio 2008, the following table N2 will help you determine which edition of Visual Studio you need.
Table N2. Features of various editions of Visual Studio 20083.2. Availability of 64-bit computers running 64-bit operating systems
You can of course use virtual machines to run 64-bit applications on 32-bit hardware, but this is extremely inconvenient and will not provide the necessary level of testing. It is desirable that the machines were installed at least 4-8 gigabytes of RAM.
3.3. Availability of 64-bit versions of all used libraries
If libraries are represented in source codes, then a 64-bit project configuration must be present. Independently engaged in upgrading the library to build it under a 64-bit system can be a thankless and difficult task, and the result may be unreliable and contain errors. You may also violate this license agreement. If you use libraries in the form of binary modules, then you also need to find out if there are 64-bit modules. You cannot use 32-bit DLLs inside a 64-bit application. You can create a special binding through COM, but this will be a separate large, complex task [
8 ]. Also note that the purchase of a 64-bit version of the library may cost extra money.
3.4. Lack of built-in assembly code
Visual C ++ does not support 64-bit inline assembly. You must use either an external 64-bit assembler (for example, MASM) or have an implementation of the same functionality in C / C ++ [
9 ].
3.5. Modernization of testing methodology
Substantial reworking of testing methodology, upgrading unit tests, using new tools. More on this will be discussed below, but do not forget to take this into account at the stage of estimating the time spent on migrating an application to a new system [
10 ].
3.6. New data for testing
If you are developing resource-intensive applications that consume a large amount of RAM, then you need to take care of updating the test input database. When load testing 64-bit applications, it is desirable to go beyond 4 gigabytes of memory consumption. Many errors can occur only under these conditions.
3.7. 64-bit security systems
Your security system must support 64-bit systems to the full extent you need. For example, Aladdin quickly released 64-bit drivers to support Hasp hardware keys. But for a very long time there was no automatic protection system for 64-bit binary files (Hasp Envelop program). Thus, the protection mechanism had to be implemented independently within the program code, which was an additional complex task that required qualifications and time. Do not forget about such moments related to security, system updates, and so on.
3.8. Installer
You must have a new installer that can fully install 64-bit applications. I would like to immediately warn about a traditional error. This is the creation of 64-bit installers for installing 32/64-bit software products. Preparing a 64-bit version of the application, developers often want to bring the “64-bit” in it to the absolute. And they create a 64-bit installer, forgetting that the users of the 32-bit operating system just do not install such an installation package. Note that the 32-bit application included in the distribution package along with the 64-bit one, namely the installer itself, will not start. After all, if a distribution kit is a 64-bit application, then on a 32-bit operating system, of course, it will not start. The most annoying thing about this is that the user will not be able to guess what is happening. He will simply see an installation package that cannot be launched.
4. Step Four. Setting up the project in Visual Studio 2005/2008
Creating a 64-bit project configuration in Visual Studio 2005/2008 looks quite simple. Difficulties will lie in wait for you at the stage of building a new configuration and searching for errors in it. To create a 64-bit configuration, it is enough to perform the following 4 steps:
Run the configuration manager, as shown in Figure N1:
Figure 1. Starting the configuration managerIn the configuration manager, we select support for the new platform (Figure N2):
Figure 2. Creating a new configurationWe select the 64-bit platform (x64), and as the basis we choose the settings from the 32-bit version (Figure N3). Those settings that affect build mode will be corrected by Visual Studio itself.
Figure 3. Choose x64 as the platform and take the Win32 configuration as the basisAdding a new configuration is completed, and we can select the 64-bit configuration option and proceed to compiling a 64-bit application. The selection of the 64-bit configuration for the assembly is shown in Figure N4.
Figure 4. Now 32-bit and 64-bit configuration is available.If you are lucky, there will be no need in addition to setting up a 64-bit project. But it strongly depends on the project, its complexity and the number of libraries used. The only thing that needs to be changed right away is the stack size. If your project uses a stack with a default size, that is, 1 megabyte, then it makes sense to set it to 2 megabytes in size for the 64-bit version. This is not necessary, but it is better to insure in advance. If you use a stack size other than the default size, then it makes sense to make it for the 64-bit version 2 times larger. To do this, in the project settings, find and change the Stack Reserve Size and Stack Commit Size.
5. Step five. Compiling the application
It would be nice to talk here about typical problems arising at the stage of compiling a 64-bit configuration. Consider what problems arise with third-party libraries, tell that the compiler in the code associated with the WInAPI functions will no longer allow the pointer to be placed in the LONG type and you will need to upgrade your code and use the LONG_PTG type. And many many others. Unfortunately, there are so many of them and the mistakes are so diverse that there is no possibility to put it in the framework of one article and even, perhaps, a book. You will have to look through all the errors that the compiler will issue and new warnings that were not there before and in each case to figure out how to upgrade the code.
A collection of links to resources devoted to the development of 64-bit applications can partially facilitate life:
http://www.viva64.com/links/64-bit-development/ . The collection is constantly updated and the author will be grateful to the readers if they send him links to resources that, in their opinion, deserve attention.
Let us dwell here only on types that may be of interest to developers during the migration of applications. These types are presented in Table N3. Most compilation errors will be associated with the use of these types.
Type of | Dimension of the type on the platform x32 / x64 | Note |
int | 32/32 | Base type On 64-bit systems, it remains 32-bit. |
long | 32/32 | Base type On 64-bit Windows systems, it remains 32-bit. Note that on 64-bit Linux systems this type has been extended to 64-bit. Do not forget about this if you are developing code that should work to compile for Windows and for Linux systems. |
size_t | 32/64 | Base unsigned type. The size of the type is chosen so that it can be written in it the maximum size of the theoretically possible array. A pointer can be safely placed in the size_t type (an exception is pointers to the functions of the classes, but this is a special case). |
ptrdiff_t | 32/64 | It is similar to the size_t type, but is signed. The result of the expression, where one pointer is subtracted from the other (ptr1-ptr2), will just be of type ptrdiff_t. |
Pointer | 32/64 | The size of the pointer depends on the bit platform. Be careful when casting pointers to other types. |
__int64 | 64/64 | Signed 64-bit type. |
DWORD | 32/32 | 32-bit unsigned type. Declared in WinDef.h as: typedef unsigned long DWORD; |
DWORDLONG | 64/64 | 64-bit unsigned type. Declared in WinNT.h as: typedef ULONGLONG DWORDLONG; |
DWORD_PTR | 32/64 | An unsigned type in which to place the pointer. Declared in BaseTsd.h as: typedef ULONG_PTR DWORD_PTR; |
DWORD32 | 32/32 | 32-bit unsigned type. Declared in BaseTsd.h as: typedef unsigned int DWORD32; |
DWORD64 | 64/64 | 64-bit unsigned type. Declared in BaseTsd.h as: typedef unsigned __int64 DWORD64; |
HALF_PTR | 16/32 | Half pointer. Declared in Basetsd.h as: #ifdef _WIN64 typedef int HALF_PTR; #else typedef short HALF_PTR; #endif |
INT_PTR | 32/64 | Sign type in which you can put a pointer. Declared in BaseTsd.h as: #if defined (_WIN64) typedef __int64 INT_PTR; #else typedef int INT_PTR; #endif |
LONG | 32/32 | The sign type that remains 32-bit. Therefore, in many cases, LONG_PTR should now be used. Declared in WinNT.h as: typedef long LONG; |
LONG_PTR | 32/64 | Sign type in which you can put a pointer. Declared in BaseTsd.h as: #if defined (_WIN64) typedef __int64 LONG_PTR; #else typedef long LONG_PTR; #endif |
LPARAM | 32/64 | Parameter for sending messages. Declared in WinNT.h as: typedef LONG_PTR LPARAM; |
SIZE_T | 32/64 | Analogue of type size_t. Declared in BaseTsd.h as: typedef ULONG_PTR SIZE_T; |
SSIZE_T | 32/64 | Analogue of type ptrdiff_t. Declared in BaseTsd.h as: typedef LONG_PTR SSIZE_T; |
ULONG_PTR | 32/64 | An unsigned type in which to place the pointer. Declared in BaseTsd.h as: #if defined (_WIN64) typedef unsigned __int64 ULONG_PTR; #else typedef unsigned long ULONG_PTR; #endif |
WORD | 16/16 | Unsigned 16-bit type. Declared in WinDef.h as: typedef unsigned short WORD; |
WPARAM | 32/64 | Parameter for sending messages. Declared in WinDef.h as: typedef UINT_PTR WPARAM; |
Table N3. Types of interest when porting 32-bit programs to 64-bit Windows systems.6. Diagnose hidden errors
If you think that after fixing all the compilation errors, a long-awaited 64-bit application will be received, then you will have to be disappointed. The most difficult thing to come. At the compilation stage, you will correct the most obvious errors that the compiler was able to detect, which are mainly related to the impossibility of implicit type conversion. But this is the tip of the iceberg. The main part of the error is hidden. From the point of view of the abstract C ++ language, these errors look safe or are masked by explicit type conversions. There are several times more such errors than the number of errors detected at the compilation stage.
Hope should not be pinned to the
/ Wp64 key. This key is often presented as a wonderful tool for searching 64-bit errors. In fact, the / Wp64 key only allows you to get some warnings when compiling a 32-bit code that in 64-bit mode certain sections of the code will be incorrect. When compiling 64-bit code, these warnings will be issued by the compiler in any case. And therefore, when compiling a 64-bit application, the / Wp64 key is ignored. And even more so this key will not help in the search for hidden errors [
11 ].
Consider a few examples of hidden errors.
6.1. Explicit type conversion
The simplest, but not the easiest to detect, class of errors is associated with the
explicit type conversion , in which truncation of significant bits occurs.
A common example is casting pointers to 32-bit types when passing them to functions such as SendMessage:
MyObj * pObj = ...
:: SendMessage (hwnd, msg, (WORD) x, (DWORD) pObj);
|
Here, an explicit type conversion is used to turn a pointer into a numeric type. For a 32-bit architecture, the given example is correct, since the last parameter of the SendMessage function is of type LPARAM, which coincides with the DWORD on the 32-bit architecture. For the 64-bit architecture, the use of the DWORD is erroneous and should be replaced with LPARAM. Type LPARAM has a size of 32 or 64 bits depending on the architecture.
This is a simple case, but often the type conversion looks more elegant and it is impossible to detect it using compiler warnings or searching through the program text. Explicit type conversions suppress compiler diagnostics, since they are precisely intended to tell the compiler that type casting is correct and the programmer took responsibility for the safety of the code. Explicit search does not help either. Types may not be standard names (set by the programmer through typedef), but there are also a few ways to implement explicit type conversions. To reliably diagnose such errors, it is necessary to use only special tools, such as
Viva64 or
PC-Lint analyzers.
6.2. Implicit type conversion
The following example is associated with
an implicit type conversion , which also results in the loss of significant bits. The fread function code reads from a file, but is incorrect when attempting to read more than 2 gigabytes of data on a 64-bit system.
size_t __fread (void * __restrict buf, size_t size,
size_t count, FILE * __restrict fp);
size_t
fread (void * __restrict buf, size_t size, size_t count,
FILE * __restrict fp)
{
int ret;
FLOCKFILE (fp);
ret = __fread (buf, size, count, fp);
FUNLOCKFILE (fp);
return (ret);
}
|
The __fread function returns the size_t type, but the int type is used to store the number of bytes read. As a result, with large amounts of read data, the function can return not the number of bytes that will actually be read.
You can say that this is an illiterate code for beginners, that the compiler will tell about this type conversion and that such a code is easy to find and fix. This is theoretically. But practically in real life with large projects everything can be different. This example is taken from the FreeBSD source code. The error was corrected only in December 2008! This is despite the fact that the first (experimental) 64-bit version of FreeBSD was released back in June 2003.
Here is the source code before the fix:
http://www.freebsd.org/cgi/cvsweb.cgi/src/lib/libc/stdio/fread.c?rev=1.14But the corrected version (December 2008) of the year:
http://www.freebsd.org/cgi/cvsweb.cgi/src/lib/libc/stdio/fread.c?rev=1.156.3. Work with bits, shifts
It is easy to make a mistake in the code working with individual bits. The next type of error is related to shift operations. Consider an example:
ptrdiff_t SetBitN (ptrdiff_t value, unsigned bitNum) {
ptrdiff_t mask = 1 << bitNum;
return value | mask;
}
|
The code above is operable on a 32-bit architecture and allows setting the bit with numbers from 0 to 31 to one. After transferring the program to the 64-bit platform, it will be necessary to set bits from 0 to 63. But this code will never set bits, with numbers 32-63. Note that “1” is of type int, and when shifted to 32 positions an overflow will occur, as shown in Figure 5. We will end up with 0 (Figure 5-B) or 1 (Figure 5-C) depending on the compiler implementation.
Figure 5. A - Correct setting of the 31st bit in a 32-bit code; B, C - Error installing the 32nd bit on a 64-bit system (two behaviors)To correct the code, it is necessary to make the constant “1” of the same type as the variable mask:
ptrdiff_t mask = ptrdiff_t (1) << bitNum;
|
Note also that an uncorrected code will result in another interesting error. When setting 31 bits on a 64-bit system, the result of the function operation will be the value 0xffffffff8 80000000 (see Figure 6). The result of the expression 1 << 31 is a negative number -2147483648. This number is represented in a 64-bit integer variable as 0xffffffff80000000.
Figure 6. Error setting the 31st bit on a 64-bit system6.4. Magic numbers
Magic constants, that is numbers with which help the size of this or that type is set can deliver many troubles. The correct solution is to use sizeof () operators for such purposes, but in a large program an old piece of code may well be lost, where they were firmly convinced that the pointer size was 4 bytes, and in the size_t type it was always 32 bits. Usually such errors look like this:
size_t ArraySize = N * 4;
size_t * Array = (size_t *) malloc (ArraySize);
|
The main numbers that should be treated with caution when switching to the 64-bit platform are given in Table N4.
Table N4. Basic magic values ​​dangerous when porting applications from 32-bit to 64-bit platform6.5. Errors using 32-bit variables as indices
In programs that process large amounts of data, errors associated with the indexing of large arrays can occur or eternal loops can occur. The following example contains 2 errors at once:
const size_t size = ...;
char * array = ...;
char * end = array + size;
for (unsigned i = 0; i! = size; ++ i)
{
const int one = 1;
end [-i - one] = 0;
}
|
The first mistake is that if the size of the processed data exceeds 4 gigabytes (0xFFFFFFFF), then an eternal loop may occur, since the variable 'i' is of the type 'unsigned' and never reaches the value of 0xFFFFFFFF. I specifically write that the occurrence is possible, but not necessarily it will happen. It depends on which code the compiler builds. For example, in the debug mode, the perpetual loop will be present, and in the release code the loop will disappear, so the compiler will decide to optimize the code using a 64-bit register for the counter and the loop will be correct. All this adds confusion, and the code that worked yesterday may suddenly stop working the next day.
The second error is related to the passage through the array from the end to the beginning, for which negative index values ​​are used. The above code is operable in 32-bit mode, but when it is started on a 64-bit machine, the first iteration of the loop will access the array and the program will crash. Consider the reason for this behavior.
According to the C ++ rule on the 32-bit system, the expression "-i - one" will be calculated as follows (in the first step i = 0):
- The expression "-i" is of the unsigned type and has the value 0x00000000u.
- The variable 'one' will be expanded from the type 'int' to the type unsigned and will be equal to 0x00000001u. Note: The int type is extended (according to the C ++ standard of the language) to the 'unsigned' type if it participates in an operation where the second argument is of the unsigned type.
- Undergo a subtraction operation in which two unsigned values ​​are involved and the result of the operation is 0x00000000u - 0x00000001u = 0xFFFFFFFFu. Note that the result is of unsigned type.
- On a 32-bit system, accessing an array at index 0xFFFFFFFFu is equivalent to using the index -1. That is, end [0xFFFFFFFFu] is analogous to end [-1]. As a result, the array element is correctly processed.
In the 64-bit system in the last paragraph, the picture will be different. The unsigned type will be expanded to the signed ptrdiff_t and the array index will be 0x00000000FFFFFFFF64. As a result, going beyond the array will occur.
To correct the code, you need to use types such as ptrdiff_t and size_t.
6.6. Errors related to changing the types of functions used
There are mistakes in which, in general, no one is to blame, but they do not cease to be mistakes from this. Imagine that a long time ago in a distant galaxy (in Visual Studio 6.0) a project was developed, in which there is a CSampleApp class, which is a successor of CWinApp. The base class has a virtual WinHelp function. The heir overrides this function and performs the necessary actions. Visually, this is represented in Figure 7.
Figure 7. Healthy valid code created in Visual Studio 6.0Then the project is transferred to Visual Studio 2005, where the prototype of the WinHelp function has changed, but no one notices this, because in the 32-bit mode, the DWORD and DWORD_PTR types are the same and the program continues to work correctly (Figure 8).
Figure 8. Incorrect but workable 32-bit codeThe error waits to manifest itself in a 64-bit system, where the size of the DWORD and DWORD_PTR types is different (Figure 9). It turns out that in the 64-bit mode the classes contain two DIFFERENT WinHelp functions, which is of course incorrect. Note that such traps can be hidden not only in MFC, where some of the functions have changed the types of their arguments, but also in the code of your applications and third-party libraries.
Figure 9. The error manifests itself in 64-bit code6.7. Diagnose hidden errors
Examples of such 64-bit errors can be given and quoted. Those who are interested in similar errors and want to know more about them will be interested in the article “20 issues of porting C ++ code on a 64-bit platform” [
12 ].
As you can see, the stage of searching for hidden errors is a non-trivial task, especially since many of them will appear irregularly or only on large input data volumes. Static code analyzers are well suited for diagnosing such errors, since they can check the entire application code, regardless of the input data and the frequency of its execution in real conditions. It makes sense to use static analysis both at the stage of transferring the application to 64-bit platforms, in order to find the majority of errors at the initial stage, and in the further development of 64-bit solutions. Static analysis will warn and teach the programmer to better understand the features of errors associated with the 64-bit architecture and to write more efficient code. The author of the article is the developer of one of these specialized code analyzers,called Viva64 [13 ]. You can get acquainted with the tool and download the demo version in more detail from the site of the company OOO “Program Verification Systems”.To be fair, it should be said that code analyzers such as Gimpel PC-Lint and Parasoft C ++ Test have sets of rules for diagnosing 64-bit errors. But, first, these are general-purpose analyzers and the rules for diagnosing 64-bit errors are poorly represented in them. Second, they are more focused on the LP64 data model used in the Linux operating system family, which reduces their usefulness for Windows programs that use the LLP64 data model [ 14 ].7. Step Seven. Modernization of the testing process
The step of finding errors in the program code described in the previous section is a necessary but insufficient step. Any method, including static code analysis, does not fully guarantee the detection of all errors and the best result can be achieved only with a combination of different techniques.If your 64-bit program processes a larger amount of data than the 32-bit version, then you need to expand the tests to include data processing of more than 4 gigabytes. This is the border beyond which many 64-bit errors begin to manifest themselves. Time such tests can take an order of magnitude more and this must be prepared in advance. Usually tests are written in such a way as to process a small number of elements in each test and thus be able to pass all internal unit tests, for example? in a few minutes, and automated tests (for example, using AutomatedQA TestComplete) in a few hours. The sorting function on a 32-bit system, if it sorts 100 elements, will almost correctly behave on 100000 elements with a full guarantee. But the same function on a 64-bit system can fail when trying to process 5 billion items. The speed of a unit test can drop millions of times. Do not forget to lay the cost of adapting the tests when mastering 64-bit systems. One solution is to split unit tests into fast (working with small memory) and slow processing gigabytes and run, for example, at night. Automated testing of resource-intensive 64-bit programs can be built on the basis of distributed computing.