chromium/tools/win/CreateTempFilesPerfEvaluation/CreateTempFilesPerfEval.cc

// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <tchar.h>
#include <windows.h>

#include <stdio.h>

#include <algorithm>
#include <iostream>
#include <sstream>
#include <string>

#include "base/win/shlwapi.h"

#pragma warning(disable : 4996)

// Create |count| number of temp files at |folder_path| using GUID based method.
// The time cost in millisecond of creating every 500 temp files or all the
// files if |count| < 500 is printed to the console.
bool CreateFilesUsingGuid(UINT count, const char* folder_path);

// Create |count| number of temp files at |folder_path| using GetTempFileName()
// API. The time cost in millisecond of creating every 500 temp files or all the
// files if |count| < 500 is printed to the console.
bool CreateFilesUsingGetTempFileName(UINT count, const char* folder_path);

// This method converts GUID to a string.
char* ConvertGuidToString(const GUID* id, char* out);

// If |folder_path| doesn't exist, creat it, otherwise check if it is empty.
bool CreateOrValidateTempDirectory(const char* folder_path);

// Deletes all the content in |folder_path|.
void DeleteDirectoryContent(const char* folder_path);

// Deletes |folder_path| including its contents and the raw directory.
bool DeleteDirectory(const char* folder_path);

// Deletes |folder_path| including its contents and the raw directory, and print
// the delete status to the console.
void DeleteDirectoryAndPrintMsg(const char* folder_path);

// Prints the elapsed time at current step for the latest cycle.
void FormatPrintElapsedTime(UINT cur_step,
                            UINT total_step,
                            const LARGE_INTEGER& elapsed_ms);

// Maximum number of temp files allowed to create. This is limited by the
// implementation of GetTempFileName().
// "This limits GetTempFileName to a maximum of 65,535 unique file names if the
// lpPathName and lpPrefixString parameters remain the same."
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa364991(v=vs.85).aspx
UINT kMaxFileCreate = 65535;

// Query the time cost each time when this amount of temp files are created.
UINT kFileCountPerMetric = 500;

int main() {
  // Gets the temp path env string.
  DWORD temp_path_ret = 0;
  CHAR temp_folder_path[MAX_PATH];
  temp_path_ret = ::GetTempPathA(MAX_PATH, temp_folder_path);
  if (temp_path_ret > MAX_PATH || temp_path_ret == 0) {
    std::cout << "GetTempPath failed" << std::endl;
    return 0;
  }

  // A temporary directory where the new temp files created by GetTempFileName()
  // are written.
  std::string temp_dir_gettempfilename(
      std::string(temp_folder_path).append("TempDirGetTempFileName\\"));

  // A temporary directory where the new temp files created by Guid-based method
  // are written.
  std::string temp_dir_guid(
      std::string(temp_folder_path).append("TempDirGuid\\"));

  UINT file_create_count;
  std::string user_input;

  while (true) {
    std::cout << "\nPlease enter # of files to create (maximum "
              << kMaxFileCreate << "), or \"quit\" to end the program : ";
    std::getline(std::cin, user_input);

    std::transform(user_input.begin(), user_input.end(), user_input.begin(),
                   ::tolower);
    if (user_input == "quit")
      break;

    std::cout << std::endl;
    std::stringstream ss(user_input);

    if (ss >> file_create_count && file_create_count <= kMaxFileCreate) {
      std::cout << "\nPlease select method to create temp file names,\n"
                << "\"t\" for GetTempFileName \n"
                << "\"g\" for GUID-based \n"
                << "\"b\" for both \n"
                << "or \"quit\" to end the program : ";
      std::getline(std::cin, user_input);

      std::transform(user_input.begin(), user_input.end(), user_input.begin(),
                     ::tolower);
      if (user_input == "quit")
        break;

      if (user_input == "t" || user_input == "b") {
        std::cout << "\nGetTempFileName Performance:\n [start -   end] / total "
                     "--- time "
                     "cost in ms"
                  << std::endl;
        if (CreateFilesUsingGetTempFileName(file_create_count,
                                            temp_dir_gettempfilename.c_str())) {
          std::cout << "File creation succeeds at " << temp_dir_gettempfilename
                    << ", now clean all of them!" << std::endl;
        }
        DeleteDirectoryAndPrintMsg(temp_dir_gettempfilename.c_str());
      }

      if (user_input == "g" || user_input == "b") {
        std::cout << "\nGUID-based Performance:\n [start -   end] / total --- "
                     "time cost in ms"
                  << std::endl;
        if (CreateFilesUsingGuid(file_create_count, temp_dir_guid.c_str())) {
          std::cout << "File creation succeeds at " << temp_dir_guid
                    << ", now clean all of them!" << std::endl;
        }
        DeleteDirectoryAndPrintMsg(temp_dir_guid.c_str());
      }
    } else {
      std::cout << "Input number is invalid, please enter # of files to create "
                   "(maximum "
                << kMaxFileCreate << "), or \"quit\" to end the program : ";
    }
    std::cout << std::endl;
  }
  return 0;
}

bool CreateFilesUsingGuid(UINT count, const char* dir_path) {
  if (!CreateOrValidateTempDirectory(dir_path))
    return false;

  LARGE_INTEGER starting_time, ending_time, elapsed_ms;
  ::QueryPerformanceCounter(&starting_time);
  LARGE_INTEGER frequency;
  ::QueryPerformanceFrequency(&frequency);

  for (UINT i = 1; i <= count; ++i) {
    GUID guid;
    ::CoCreateGuid(&guid);
    char buffer[37];
    ConvertGuidToString(&guid, buffer);
    std::string temp_name = std::string(dir_path).append(buffer).append(".tmp");

    HANDLE file_handle =
        ::CreateFileA(temp_name.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL,
                      CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    ::CloseHandle(file_handle);

    if (i % kFileCountPerMetric == 0 || i == count) {
      ::QueryPerformanceCounter(&ending_time);
      // Convert the elapsed number of ticks to milliseconds.
      elapsed_ms.QuadPart = (ending_time.QuadPart - starting_time.QuadPart) *
                            1000 / frequency.QuadPart;

      FormatPrintElapsedTime(i, count, elapsed_ms);

      ::QueryPerformanceCounter(&starting_time);
    }
  }
  return true;
}

bool CreateFilesUsingGetTempFileName(UINT count, const char* dir_path) {
  if (!CreateOrValidateTempDirectory(dir_path))
    return false;

  CHAR temp_name[MAX_PATH];
  LARGE_INTEGER starting_time, ending_time, elapsed_ms;
  ::QueryPerformanceCounter(&starting_time);
  LARGE_INTEGER frequency;
  ::QueryPerformanceFrequency(&frequency);

  for (UINT i = 1; i <= count; ++i) {
    ::GetTempFileNameA(dir_path, "", 0, temp_name);
    if (i % kFileCountPerMetric == 0 || i == count) {
      ::QueryPerformanceCounter(&ending_time);
      // Convert the elapsed number of ticks to milliseconds.
      elapsed_ms.QuadPart = (ending_time.QuadPart - starting_time.QuadPart) *
                            1000 / frequency.QuadPart;

      FormatPrintElapsedTime(i, count, elapsed_ms);

      ::QueryPerformanceCounter(&starting_time);
    }
  }
  return true;
}

char* ConvertGuidToString(const GUID* id, char* out) {
  int i;
  char* ret = out;
  out += sprintf(out, "%.8lX-%.4hX-%.4hX-", id->Data1, id->Data2, id->Data3);
  for (i = 0; i < sizeof(id->Data4); ++i) {
    out += sprintf(out, "%.2hhX", id->Data4[i]);
    if (i == 1)
      *(out++) = '-';
  }
  return ret;
}

bool CreateOrValidateTempDirectory(const char* folder_path) {
  if (::PathFileExistsA(folder_path)) {
    if (!::PathIsDirectoryEmptyA(folder_path)) {
      std::cout << folder_path
                << " directory is not empty, please remove all its content.";
      return false;
    }
    return true;
  } else if (::CreateDirectoryA(folder_path, NULL) == 0) {
    std::cout << folder_path << "directory creation fails.";
    return false;
  } else {
    return true;
  }
}

void DeleteDirectoryContent(const char* folder_path) {
  char file_found[MAX_PATH];
  WIN32_FIND_DATAA info;
  HANDLE hp;
  sprintf(file_found, "%s\\*.*", folder_path);
  hp = ::FindFirstFileA(file_found, &info);
  do {
    if ((strcmp(info.cFileName, ".") == 0) ||
        (strcmp(info.cFileName, "..") == 0)) {
      continue;
    }
    sprintf(file_found, "%s\\%s", folder_path, info.cFileName);
    ::DeleteFileA(file_found);
  } while (::FindNextFileA(hp, &info));
  ::FindClose(hp);
}

bool DeleteDirectory(const char* folder_path) {
  DeleteDirectoryContent(folder_path);
  return ::RemoveDirectoryA(folder_path) != 0;
}

void DeleteDirectoryAndPrintMsg(const char* folder_path) {
  if (DeleteDirectory(folder_path)) {
    std::cout << folder_path << " directory is deleted!" << std::endl;
  } else {
    std::cout << "[Attention] " << folder_path
              << " directory's deletion fails, please take a look by yourself!"
              << std::endl;
  }
}

void FormatPrintElapsedTime(UINT cur_step,
                            UINT total_step,
                            const LARGE_INTEGER& elapsed_ms) {
  UINT count_prev = 0;
  if (cur_step % kFileCountPerMetric == 0)
    count_prev = cur_step + 1 - kFileCountPerMetric;
  else if (cur_step > kFileCountPerMetric)
    count_prev = cur_step / kFileCountPerMetric * kFileCountPerMetric + 1;
  printf(" [%5d - %5d] / %d --- %lld\n", count_prev, cur_step, total_step,
         elapsed_ms.QuadPart);
}