#!/usr/bin/env ruby # #Copyright (c) 2009, Perforce Software, Inc. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL PERFORCE SOFTWARE, INC. BE LIABLE FOR ANY # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #******************************************************************************* # #Author: Stephen Moon # # encoding: utf-8 require 'thread' require 'logger' class Mthreads def initialize(num_threads,port,user) @num_threads = num_threads @p4port = port @p4user = user @cmd_prefix = "p4 -p #{@p4port} -u #{@p4user}" @dir_name = "thread_files" @cmds_array = [] @cmds_map = {} end def create_file(fcount, log) first_Dir = File.join(File::SEPARATOR,Dir.pwd,@dir_name) log.debug(first_Dir) if !File.directory?(first_Dir) Dir.mkdir(first_Dir) end second_Dir = File.join(File::SEPARATOR,first_Dir,fcount.to_s) log.debug(second_Dir) if !File.directory?(second_Dir) Dir.mkdir(second_Dir) end nFile = "" if self.depot_exists(log) != 1 third_Dir = File.join(File::SEPARATOR,second_Dir,'depot') log.debug(third_Dir) if !File.directory?(third_Dir) Dir.mkdir(third_Dir) end nFile = File.join(File::SEPARATOR,third_Dir,fcount.to_s) else nFile = File.join(File::SEPARATOR,second_Dir,fcount.to_s) end log.debug(nFile) file_write = File.new(nFile,'w') file_write.puts '$File:$' file_write.puts 'File ID: $Id$' file_write.puts 'File Header: $Header$' file_write.puts 'File Author: $Author$' file_write.puts 'File Date: $Date$' file_write.puts 'File DateTime: $DateTime$' file_write.puts 'File Change: $Change$' file_write.puts 'File File: $File$' file_write.puts 'File Revision: $Revision$' count = 0 while(count < 10000000) file_write.print "This is " + fcount.to_s + "\n\n" file_write.print "Loop count is " + count.to_s + "\n\n" count += 1 end file_write.flush file_write.close return nFile end def get_filename(count, log) nFile = "" if self.depot_exists(log) != 1 nFile = File.join(File::SEPARATOR,Dir.pwd,@dir_name,count.to_s,'depot',count.to_s) else nFile = File.join(File::SEPARATOR,Dir.pwd,@dir_name,count.to_s,count.to_s) end log.debug(nFile) return nFile end def depot_exists(log) number = self.join_passed_array_n_exec([@cmd_prefix,'depots'],log) count = depot = 0 number.each { |n| count += 1 #print "depot: #{n}\n" if n.match(/^Depot\s(\S+)\s.*$/) if $1 == "depot" depot = 1 end end } if count > 1 and depot == 1 #threads_file/0/depot/0 return 0 elsif count == 1 and depot == 1 #threads_file/0/0 return 1 elsif count > 1 and depot != 1 #first make depot "depot" and then threads_file/0/depot/0 return 2 end end def file_add(client_name, filename, log) self.join_passed_array_n_exec([@cmd_prefix,'-c',client_name,'add','-t','+k',filename],log) self.join_passed_array_n_exec([@cmd_prefix,'-c',client_name,'submit','-d','added_' + filename,filename],log) log.info(filename + " added successfully") end def create_client(count, log) nclient_array = Array.new output = self.join_passed_array_n_exec([@cmd_prefix,'client','-o','c' + count.to_s],log) log.debug(output) output.each { |n| if n.match(/(^Root:\s+)(\S+)$/) prefix = $1; suffix = $2 c = File.join(File::SEPARATOR,suffix,@dir_name,count.to_s) nclient_array.push(prefix + c) else nclient_array.push(n) end } newspec = nclient_array.join log.debug(newspec) IO.popen("#{@cmd_prefix} client -i",'w') { |pipe| pipe.print(newspec) pipe.close } return ('c' + count.to_s) end def cmds_hash(count, log) client_name = "-c c" + count.to_s needs_resolve = ['merge','copy','ignore','move'] no_need_to_submit = ['print','fstat','filelog','populate','annotate','sync'] filename = self.get_filename(count,log) log.info("Filename: " + filename) integ_cmds_map = { 'merge' => 'integ;resolve -am', 'copy' => 'integ;resolve -at', 'ignore' => 'integ;resolve -ay', 'move' => 'move;resolve -at', 'populate' => 'populate', 'branch' => 'integ' } #randonmized test case is split one character at a time #and then it is mapped to a command according to the dictionary #in cmds_db method @cmds_array[rand(@cmds_array.length)].split("").each { |c| #splits the command and arg number delimited by ":" if @cmds_map[c].match(/(\w+)\:(\w+)/) cmd_name = $1; args = $2 log.info("cmd: " + cmd_name + " args: " + args) if args.to_i == 2 if needs_resolve.include?(cmd_name) (cmd_name, resolve_cmd) = integ_cmds_map[cmd_name].split(";") a = self.join_passed_array_n_exec([@cmd_prefix, client_name, cmd_name, filename,filename + count.to_s],log) puts puts a log.info("Integ command: ") log.info(a) b = self.join_passed_array_n_exec([@cmd_prefix, client_name, resolve_cmd],log) puts puts b log.info("Resolve command: ") log.info(b) else cmd_name = integ_cmds_map[cmd_name] c = self.join_passed_array_n_exec([@cmd_prefix, client_name,cmd_name, filename,filename + count.to_s],log) puts puts c log.info("Integ without resolve: ") log.info(c) end else log.info("cmd: " + cmd_name + " filename: " + filename) d = '' if cmd_name == 'sync' d = self.join_passed_array_n_exec([@cmd_prefix, client_name, cmd_name,' -f ', filename],log) elsif cmd_name == 'print' d = self.join_passed_array_n_exec([@cmd_prefix, client_name, cmd_name, filename, ' > /dev/null'],log) else d = self.join_passed_array_n_exec([@cmd_prefix, client_name, cmd_name, filename],log) end puts puts d log.info("No submit: ") log.info(d) end #end of different arg for different cmds #sleep(1) if args.to_i == 2 e = self.join_passed_array_n_exec([@cmd_prefix, client_name, 'submit', '-d', cmd_name + '_' + filename + '_' + filename + count.to_s],log) puts puts e log.info("Two arg submit: ") log.info(e) elsif (!no_need_to_submit.include?(cmd_name)) f = self.join_passed_array_n_exec([@cmd_prefix, client_name, 'submit', '-d', cmd_name + '_' + filename],log) puts puts f log.info("One arg submit: ") log.info(f) end #end of submit end #end of regex match statement } end def revert_all_files(log) files = self.join_passed_array_n_exec([@cmd_prefix,'revert','//depot/...'],log) puts files log.debug(files) end def obliterate_all_files(log) ofiles = self.join_passed_array_n_exec([@cmd_prefix,'obliterate','-y','//depot/...'],log) puts ofiles log.debug(ofiles) end def remove_all_client_files(log) cfiles = self.join_passed_array_n_exec(['rm','-rf','thread_files'],log) puts cfiles log.debug(cfiles) end def delete_clients(log) clients = self.join_passed_array_n_exec([@cmd_prefix,'clients'],log) #puts clients log.debug(clients) clients.each { |n| if n.force_encoding("UTF-8").ascii_only? if n.match(/^Client\s+(c\S+)\s+.*$/) self.join_passed_array_n_exec([@cmd_prefix,'client','-f','-d',$1],log) puts "Client #{$1} deleted" log.debug("Client #{$1} deleted") end end } end def delete_pending_changes(log) changes = self.join_passed_array_n_exec([@cmd_prefix,'changes','-s','pending','//depot/...'],log) log.debug(changes) changes.each { |n| puts n if n.match(/^Change\s(\S+)\son\s\d{4}\/\d{2}\/\d{2}\sby\s\S+\@(\S+)\s\*pending\*\s.*$/) self.join_passed_array_n_exec([@cmd_prefix,'-c',$2,'change','-d',$1],log) puts "Change #{$1} deleted" log.debug("Change #{$1} deleted") end } end def join_passed_array_n_exec(array, log) o_array = Array.new joined_cmd = array.join(' ') log.debug(joined_cmd) `#{joined_cmd}`.lines { |n| o_array.push(n + "\n") } return o_array end def cmds_db @cmds_array = ['spf'] @cmds_map = {'a' => 'add:1', 'b' => 'branch:2', 'c' => 'copy:2', 'd' => 'delete:1', 'e' => 'edit:1', 'f' => 'fstat:1', 'g' => 'ignore:2', 'i' => 'integ:2', 'l' => 'filelog:1', 'm' => 'merge:2', 'o' => 'annotate:1', #'p' => 'populate:2', 'p' => 'print:1', #'r' => 'move:2', 's' => 'sync:1' } end end if ARGV.count != 3 puts "Usage: ruby mthreads.rb " puts puts "e.g. ruby mthreads.rb 10 1111 smoon" exit(1) end nthread = Mthreads.new(ARGV[0],ARGV[1],ARGV[2]) threads = [] num_threads = (ARGV[0]).to_i log_file = ARGV[2] + "_" + ARGV[1] + "_" + ARGV[0] + ".log" log_file = File.join(File::SEPARATOR,Dir.pwd, log_file) fd_log = File.open(log_file, File::WRONLY | File::APPEND | File::CREAT) log = Logger.new(fd_log) log.level = Logger::INFO nthread.revert_all_files(log) nthread.obliterate_all_files(log) nthread.remove_all_client_files(log) nthread.delete_pending_changes(log) nthread.delete_clients(log) nFile = '' num_threads.times { |f| nFile = nthread.create_file(f,log) client_name = nthread.create_client(f,log) nthread.file_add(client_name, nFile,log) } puts "Created clients and added files" nthread.cmds_db() puts "Initialized db" begin cthread = Thread.new do count = 0 num_threads.times { |i| Thread.current['tCount'] = count nthread.cmds_hash(count,log) count += 1 } end threads << cthread threads.each { |thread| cthread.join; puts cthread['tCount'] } log.close() rescue => ex log.fatal(ex.message) puts "#{ex.class}: #{ex.message}" log.close() end